loko/streetup/schools/test_internal_geocoding.py
2026-07-22 14:48:40 +02:00

230 lines
8.5 KiB
Python

from django.test import TestCase
from common.models import Municipality, AdrStreet, AdrPoint
from django.contrib.gis.geos import Point
from .internal_geocoding import (
normalize_address,
search_address_points,
search_streets,
geocode_internal,
DEFAULT_SCORE_THRESHOLD
)
class NormalizeAddressTestCase(TestCase):
"""Test address normalization"""
def test_normalize_empty_address(self):
self.assertEqual(normalize_address(""), "")
self.assertEqual(normalize_address(None), "")
def test_normalize_removes_extra_spaces(self):
self.assertEqual(normalize_address(" rue de la loi "), "rue de la loi")
def test_normalize_lowercase(self):
self.assertEqual(normalize_address("Rue DE La LOI"), "rue de la loi")
class InternalGeocodingTestCase(TestCase):
"""Test internal geocoding with fuzzy matching"""
def setUp(self):
"""Create test data"""
# Create a municipality
self.municipality = Municipality.objects.create(
fid="1000",
name_fr="Bruxelles",
name_nl="Brussel",
niscode="21004"
)
# Create a street
self.street = AdrStreet.objects.create(
fid="street_001",
name_fr="Rue de la Loi",
name_nl="Wetstraat",
full_street_fr="Rue de la Loi",
full_street_nl="Wetstraat",
municipality=self.municipality,
lon=4.3676,
lat=50.8467,
centroid=Point(4.3676, 50.8467, srid=3812)
)
# Create address points
self.point1 = AdrPoint.objects.create(
fid="point_001",
number="16",
street_name=self.street,
municipality=self.municipality,
zipcode="1000",
full_address_fr="16 Rue de la Loi Bruxelles",
full_address_nl="16 Wetstraat Brussel",
lon=4.3676,
lat=50.8467,
geom=Point(4.3676, 50.8467, srid=3812)
)
self.point2 = AdrPoint.objects.create(
fid="point_002",
number="100",
street_name=self.street,
municipality=self.municipality,
zipcode="1000",
full_address_fr="100 Rue de la Loi Bruxelles",
full_address_nl="100 Wetstraat Brussel",
lon=4.3680,
lat=50.8470,
geom=Point(4.3680, 50.8470, srid=3812)
)
def test_search_address_points_exact_match(self):
"""Test searching for an exact address match"""
results = search_address_points("16 Rue de la Loi Bruxelles", limit=5)
self.assertGreater(len(results), 0)
point, score = results[0]
self.assertEqual(point.fid, "point_001")
self.assertGreater(score, 90) # Should be a very high score
def test_search_address_points_french(self):
"""Test searching in French"""
results = search_address_points("16 rue de la loi", limit=5)
self.assertGreater(len(results), 0)
point, score = results[0]
self.assertEqual(point.number, "16")
self.assertGreater(score, 60)
def test_search_address_points_dutch(self):
"""Test searching in Dutch"""
results = search_address_points("16 wetstraat", limit=5)
self.assertGreater(len(results), 0)
point, score = results[0]
self.assertEqual(point.number, "16")
self.assertGreater(score, 60)
def test_search_streets_french(self):
"""Test searching for streets in French"""
results = search_streets("Rue de la Loi", limit=5)
self.assertGreater(len(results), 0)
street, score = results[0]
self.assertEqual(street.fid, "street_001")
self.assertGreater(score, 90)
def test_search_streets_dutch(self):
"""Test searching for streets in Dutch"""
results = search_streets("Wetstraat", limit=5)
self.assertGreater(len(results), 0)
street, score = results[0]
self.assertEqual(street.fid, "street_001")
self.assertGreater(score, 90)
def test_geocode_internal_success(self):
"""Test successful internal geocoding"""
lon, lat, found_addr, score = geocode_internal("16 Rue de la Loi", threshold=60)
self.assertIsNotNone(lon)
self.assertIsNotNone(lat)
self.assertIn("16", found_addr)
self.assertIn("Rue de la Loi", found_addr)
self.assertGreater(score, 60)
def test_geocode_internal_street_only(self):
"""Test geocoding with street name only (no house number)"""
lon, lat, found_addr, score = geocode_internal("Rue de la Loi Bruxelles", threshold=60)
self.assertIsNotNone(lon)
self.assertIsNotNone(lat)
self.assertIn("Rue de la Loi", found_addr)
self.assertGreater(score, 60)
def test_geocode_internal_below_threshold(self):
"""Test that low-scoring matches are rejected"""
# Use a very high threshold that won't be met
lon, lat, found_addr, score = geocode_internal("16 Rue de la Loi", threshold=99)
# Should return None if score is below threshold
# (assuming the score is less than 99)
if score < 99:
self.assertIsNone(lon)
self.assertIsNone(lat)
def test_geocode_internal_no_match(self):
"""Test geocoding with no matching address"""
lon, lat, found_addr, score = geocode_internal("Nonexistent Street 999", threshold=80)
self.assertIsNone(lon)
self.assertIsNone(lat)
self.assertEqual(score, 0)
def test_geocode_internal_empty_address(self):
"""Test geocoding with empty address"""
lon, lat, found_addr, score = geocode_internal("", threshold=80)
self.assertIsNone(lon)
self.assertIsNone(lat)
self.assertEqual(score, 0)
def test_geocode_internal_fuzzy_match(self):
"""Test fuzzy matching with typos"""
# Test with a typo: "Rua" instead of "Rue"
lon, lat, found_addr, score = geocode_internal("16 Rua de la Loi", threshold=70)
# Should still find a match with a reasonable score
if score >= 70:
self.assertIsNotNone(lon)
self.assertIsNotNone(lat)
def test_geocode_internal_partial_match(self):
"""Test partial address matching"""
lon, lat, found_addr, score = geocode_internal("loi 16", threshold=70)
# Should find a match even with different word order
if score >= 70:
self.assertIsNotNone(lon)
self.assertIsNotNone(lat)
class SearchLimitTestCase(TestCase):
"""Test search limit functionality"""
def setUp(self):
"""Create multiple test addresses"""
self.municipality = Municipality.objects.create(
fid="1000",
name_fr="Bruxelles",
name_nl="Brussel",
niscode="21004"
)
self.street = AdrStreet.objects.create(
fid="street_001",
name_fr="Rue de la Loi",
name_nl="Wetstraat",
full_street_fr="Rue de la Loi",
full_street_nl="Wetstraat",
municipality=self.municipality,
lon=4.3676,
lat=50.8467,
centroid=Point(4.3676, 50.8467, srid=3812)
)
# Create 5 address points
for i in range(1, 6):
number = str(i * 10)
AdrPoint.objects.create(
fid=f"point_00{i}",
number=number,
street_name=self.street,
municipality=self.municipality,
zipcode="1000",
full_address_fr=f"{number} Rue de la Loi Bruxelles",
full_address_nl=f"{number} Wetstraat Brussel",
lon=4.3676 + i * 0.001,
lat=50.8467 + i * 0.001,
geom=Point(4.3676 + i * 0.001, 50.8467 + i * 0.001, srid=3812)
)
def test_search_respects_limit(self):
"""Test that search limit is respected"""
results = search_address_points("Rue de la Loi", limit=3)
self.assertLessEqual(len(results), 3)
def test_search_returns_best_matches(self):
"""Test that search returns best matches first"""
results = search_address_points("Rue de la Loi 10", limit=5)
if len(results) > 1:
# Scores should be in descending order
scores = [score for _, score in results]
self.assertEqual(scores, sorted(scores, reverse=True))