refactor: automate street synchronization and spatial inheritance for sign poles and panels
This commit is contained in:
parent
2c207fcfbb
commit
48d7796e45
4 changed files with 429 additions and 49 deletions
|
|
@ -25,7 +25,9 @@ class SignPoleForm(forms.ModelForm):
|
||||||
lat = self.cleaned_data.get('lat')
|
lat = self.cleaned_data.get('lat')
|
||||||
lon = self.cleaned_data.get('lon')
|
lon = self.cleaned_data.get('lon')
|
||||||
if lat and lon:
|
if lat and lon:
|
||||||
instance.geom = Point(lon, lat, srid=4326)
|
instance.lon = float(lon)
|
||||||
|
instance.lat = float(lat)
|
||||||
|
instance.geom = Point(float(lon), float(lat), srid=4326)
|
||||||
instance.geojson = instance.geom.geojson
|
instance.geojson = instance.geom.geojson
|
||||||
instance.geom.transform(3812)
|
instance.geom.transform(3812)
|
||||||
if commit:
|
if commit:
|
||||||
|
|
@ -59,7 +61,11 @@ class SignPanelForm(forms.ModelForm):
|
||||||
lat = self.cleaned_data.get('lat')
|
lat = self.cleaned_data.get('lat')
|
||||||
lon = self.cleaned_data.get('lon')
|
lon = self.cleaned_data.get('lon')
|
||||||
if lat and lon:
|
if lat and lon:
|
||||||
instance.geom = Point(lon, lat, srid=4326)
|
instance.lon = float(lon)
|
||||||
|
instance.lat = float(lat)
|
||||||
|
instance.geom = Point(float(lon), float(lat), srid=4326)
|
||||||
|
instance.geojson = instance.geom.geojson
|
||||||
|
instance.geom.transform(3812)
|
||||||
if commit:
|
if commit:
|
||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,19 @@ class SignStreet(AbstractLocation):
|
||||||
street_level = models.CharField(max_length=50, null=True, blank=True)
|
street_level = models.CharField(max_length=50, null=True, blank=True)
|
||||||
|
|
||||||
def get_active_assets(self):
|
def get_active_assets(self):
|
||||||
signpoles = self.signpoles.filter(status="active")
|
from django.db.models import Q
|
||||||
signpanels = self.signpanels.filter(status="active")
|
if hasattr(self, 'signpoles'):
|
||||||
|
signpoles = self.signpoles.filter(status="active")
|
||||||
|
signpanels = self.signpanels.filter(status="active")
|
||||||
|
else:
|
||||||
|
poles_qs = SignPole.objects.filter(status="active")
|
||||||
|
panels_qs = SignPanel.objects.filter(status="active")
|
||||||
|
if self.geom:
|
||||||
|
signpoles = poles_qs.filter(Q(geom__intersects=self.geom) | Q(street__name_fr__iexact=self.name_fr)).distinct()
|
||||||
|
signpanels = panels_qs.filter(Q(geom__intersects=self.geom) | Q(street__name_fr__iexact=self.name_fr)).distinct()
|
||||||
|
else:
|
||||||
|
signpoles = poles_qs.filter(street__name_fr__iexact=self.name_fr)
|
||||||
|
signpanels = panels_qs.filter(street__name_fr__iexact=self.name_fr)
|
||||||
return {"signpoles": signpoles, "signpanels": signpanels}
|
return {"signpoles": signpoles, "signpanels": signpanels}
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
|
@ -153,6 +164,23 @@ class SignPole(AbstractGeoAsset):
|
||||||
class Meta:
|
class Meta:
|
||||||
indexes = [models.Index(fields=["code"])]
|
indexes = [models.Index(fields=["code"])]
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
if self.geom and hasattr(self, 'sign_app_panels'):
|
||||||
|
for panel in self.sign_app_panels.all():
|
||||||
|
needs_save = False
|
||||||
|
if panel.geom != self.geom or panel.lon != self.lon or panel.lat != self.lat:
|
||||||
|
panel.geom = self.geom
|
||||||
|
panel.lon = self.lon
|
||||||
|
panel.lat = self.lat
|
||||||
|
panel.geojson = self.geojson
|
||||||
|
needs_save = True
|
||||||
|
if panel.street_id != self.street_id and self.street_id:
|
||||||
|
panel.street = self.street
|
||||||
|
needs_save = True
|
||||||
|
if needs_save:
|
||||||
|
panel.save()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("sign:sign_assets_detail", kwargs={"asset_model": "signpole", "asset_id": self.id})
|
return reverse("sign:sign_assets_detail", kwargs={"asset_model": "signpole", "asset_id": self.id})
|
||||||
|
|
||||||
|
|
@ -239,6 +267,26 @@ class SignPanel(AbstractGeoAsset):
|
||||||
class Meta:
|
class Meta:
|
||||||
indexes = [models.Index(fields=["code"])]
|
indexes = [models.Index(fields=["code"])]
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
if self.pole and getattr(self.pole, 'geom', None):
|
||||||
|
self.geom = self.pole.geom
|
||||||
|
self.lon = self.pole.lon
|
||||||
|
self.lat = self.pole.lat
|
||||||
|
self.geojson = self.pole.geojson
|
||||||
|
if self.pole and self.pole.street and not self.street_id:
|
||||||
|
self.street = self.pole.street
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.pole and getattr(self.pole, 'geom', None):
|
||||||
|
self.geom = self.pole.geom
|
||||||
|
self.lon = self.pole.lon
|
||||||
|
self.lat = self.pole.lat
|
||||||
|
self.geojson = self.pole.geojson
|
||||||
|
if self.pole and self.pole.street and not self.street_id:
|
||||||
|
self.street = self.pole.street
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("sign:sign_assets_detail", kwargs={"asset_model": "signpanel", "asset_id": self.id})
|
return reverse("sign:sign_assets_detail", kwargs={"asset_model": "signpanel", "asset_id": self.id})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -784,3 +784,219 @@ class CopySignDataCommandTests(TestCase):
|
||||||
self.assertEqual(SignPole.objects.filter(code="POL-001").count(), 1)
|
self.assertEqual(SignPole.objects.filter(code="POL-001").count(), 1)
|
||||||
self.assertEqual(SignPanel.objects.filter(code="PAN-001").count(), 1)
|
self.assertEqual(SignPanel.objects.filter(code="PAN-001").count(), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class SignStreetAssignmentTests(TestCase):
|
||||||
|
"""Tests for resolving AdrStreet and avoiding SignStreet assignment errors."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
from django.contrib.gis.geos import Point, LineString, Polygon, MultiPolygon
|
||||||
|
from common.models import Municipality, AdrStreet, AppView, UserConfig, Thematic, UserThematics
|
||||||
|
from assets.models import AssetCategory
|
||||||
|
from sign.models import (
|
||||||
|
SignPoleModel,
|
||||||
|
SignPanelModel,
|
||||||
|
SignPanelType,
|
||||||
|
SignStreet,
|
||||||
|
Organization,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
# Create sign view and thematic
|
||||||
|
self.sign_view, _ = AppView.objects.get_or_create(
|
||||||
|
code='sign',
|
||||||
|
defaults={
|
||||||
|
'name_fr': 'Signalisation',
|
||||||
|
'name_nl': 'Signalisatie',
|
||||||
|
'url_name': 'sign:index',
|
||||||
|
'order': 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.sign_thematic, _ = Thematic.objects.get_or_create(
|
||||||
|
code='sign',
|
||||||
|
defaults={
|
||||||
|
'name_fr': 'Signalisation',
|
||||||
|
'name_nl': 'Signalisatie',
|
||||||
|
'color': '#0022ff',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Admin user with full edit access
|
||||||
|
self.user = User.objects.create_superuser(
|
||||||
|
username='admin_sign',
|
||||||
|
password='testpass123',
|
||||||
|
email='admin@test.com'
|
||||||
|
)
|
||||||
|
self.config = UserConfig.objects.create(user=self.user)
|
||||||
|
self.config.accessible_views.add(self.sign_view)
|
||||||
|
UserThematics.objects.create(
|
||||||
|
user_config=self.config,
|
||||||
|
thematic=self.sign_thematic,
|
||||||
|
can_view_assets=True,
|
||||||
|
can_edit_assets=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Organization
|
||||||
|
self.org = Organization.objects.create(
|
||||||
|
code="GAN",
|
||||||
|
name_fr="Ganshoren",
|
||||||
|
name_nl="Ganshoren",
|
||||||
|
organization_type="municipality",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Category
|
||||||
|
self.category = AssetCategory.objects.create(
|
||||||
|
code="sign_cat_test",
|
||||||
|
name_fr="Catégorie Signalisation Test",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Municipality
|
||||||
|
self.muni = Municipality.objects.create(
|
||||||
|
fid="1083",
|
||||||
|
name_fr="Ganshoren",
|
||||||
|
name_nl="Ganshoren",
|
||||||
|
)
|
||||||
|
|
||||||
|
# AdrStreet (the actual address street in common)
|
||||||
|
poly = Polygon(((145000, 172000), (145000, 172500), (145500, 172500), (145500, 172000), (145000, 172000)))
|
||||||
|
self.adr_street = AdrStreet.objects.create(
|
||||||
|
fid="ADR-GAN-001",
|
||||||
|
name_fr="Avenue Charles-Quint",
|
||||||
|
name_nl="Keizer Karellaan",
|
||||||
|
municipality=self.muni,
|
||||||
|
geom=MultiPolygon(poly, srid=3812),
|
||||||
|
centroid=Point(145250, 172250, srid=3812),
|
||||||
|
)
|
||||||
|
|
||||||
|
# SignStreet (the road network geometry layer)
|
||||||
|
self.sign_street = SignStreet.objects.create(
|
||||||
|
code="GAN_R20_001",
|
||||||
|
name_fr="Avenue Charles-Quint",
|
||||||
|
name_nl="Keizer Karellaan",
|
||||||
|
municipality=self.muni,
|
||||||
|
geom=MultiPolygon(poly, srid=3812),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pole and panel models
|
||||||
|
self.pole_model = SignPoleModel.objects.create(
|
||||||
|
code="PM-TEST",
|
||||||
|
name_fr="Poteau Standard Test",
|
||||||
|
category=self.category,
|
||||||
|
height=3.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.panel_model = SignPanelModel.objects.create(
|
||||||
|
code="PANM-TEST",
|
||||||
|
name_fr="Modèle C1 Test",
|
||||||
|
category=self.category,
|
||||||
|
shape="rond",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.panel_type = SignPanelType.objects.create(
|
||||||
|
code="C1",
|
||||||
|
name_fr="Sens interdit",
|
||||||
|
name_nl="Verboden richting",
|
||||||
|
default_width=60.0,
|
||||||
|
default_height=60.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_closest_adr_street(self):
|
||||||
|
from sign.views import get_closest_adr_street
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
# Test resolving using SignStreet directly
|
||||||
|
adr = get_closest_adr_street(sign_street=self.sign_street)
|
||||||
|
self.assertEqual(adr, self.adr_street)
|
||||||
|
|
||||||
|
# Test resolving using spatial coords in Lambert 2008 (3812)
|
||||||
|
pt_3812 = Point(145250, 172250, srid=3812)
|
||||||
|
adr_pt = get_closest_adr_street(point=pt_3812)
|
||||||
|
self.assertEqual(adr_pt, self.adr_street)
|
||||||
|
|
||||||
|
def test_sign_pole_create_assigns_adr_street(self):
|
||||||
|
"""Test adding a pole at Charles-Quint does not crash with SignStreet assignment error."""
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
pt = Point(145250, 172250, srid=3812)
|
||||||
|
pt.transform(4326)
|
||||||
|
|
||||||
|
response = self.client.post(reverse('sign:sign_pole_create'), {
|
||||||
|
'model': self.pole_model.id,
|
||||||
|
'organization': self.org.id,
|
||||||
|
'orientation': 0,
|
||||||
|
'lat': pt.y,
|
||||||
|
'lon': pt.x,
|
||||||
|
})
|
||||||
|
self.assertEqual(response.status_code, 200, response.content)
|
||||||
|
data = response.json()
|
||||||
|
self.assertTrue(data.get('success'))
|
||||||
|
|
||||||
|
from sign.models import SignPole
|
||||||
|
pole = SignPole.objects.get(id=data['id'])
|
||||||
|
# Must be an instance of AdrStreet, not SignStreet
|
||||||
|
from common.models import AdrStreet
|
||||||
|
self.assertIsInstance(pole.street, AdrStreet)
|
||||||
|
self.assertEqual(pole.street, self.adr_street)
|
||||||
|
|
||||||
|
def test_sign_panel_create_assigns_adr_street(self):
|
||||||
|
"""Test adding a panel at Charles-Quint assigns AdrStreet correctly."""
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
pt = Point(145250, 172250, srid=3812)
|
||||||
|
pt.transform(4326)
|
||||||
|
|
||||||
|
response = self.client.post(reverse('sign:sign_panel_create'), {
|
||||||
|
'signpanel_type': self.panel_type.id,
|
||||||
|
'model': self.panel_model.id,
|
||||||
|
'organization': self.org.id,
|
||||||
|
'orientation': 0,
|
||||||
|
'support_type': 'pole',
|
||||||
|
'lat': pt.y,
|
||||||
|
'lon': pt.x,
|
||||||
|
})
|
||||||
|
self.assertEqual(response.status_code, 200, response.content)
|
||||||
|
data = response.json()
|
||||||
|
self.assertTrue(data.get('success'))
|
||||||
|
|
||||||
|
from sign.models import SignPanel
|
||||||
|
panel = SignPanel.objects.get(id=data['id'])
|
||||||
|
from common.models import AdrStreet
|
||||||
|
self.assertIsInstance(panel.street, AdrStreet)
|
||||||
|
self.assertEqual(panel.street, self.adr_street)
|
||||||
|
|
||||||
|
def test_sign_panel_create_attached_to_pole(self):
|
||||||
|
"""Test adding a panel attached to an existing pole inherits pole's street and sets vertical order."""
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
from sign.models import SignPole, SignPanel
|
||||||
|
from common.models import AdrStreet
|
||||||
|
|
||||||
|
pt = Point(145250, 172250, srid=3812)
|
||||||
|
pole = SignPole.objects.create(
|
||||||
|
street=self.adr_street,
|
||||||
|
model=self.pole_model,
|
||||||
|
organization=self.org,
|
||||||
|
geom=pt,
|
||||||
|
lon=4.31,
|
||||||
|
lat=50.86,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.post(reverse('sign:sign_panel_create'), {
|
||||||
|
'pole_id': pole.id,
|
||||||
|
'signpanel_type': self.panel_type.id,
|
||||||
|
'model': self.panel_model.id,
|
||||||
|
'organization': self.org.id,
|
||||||
|
'orientation': 0,
|
||||||
|
'support_type': 'pole',
|
||||||
|
})
|
||||||
|
self.assertEqual(response.status_code, 200, response.content)
|
||||||
|
data = response.json()
|
||||||
|
self.assertTrue(data.get('success'))
|
||||||
|
|
||||||
|
panel = SignPanel.objects.get(id=data['id'])
|
||||||
|
self.assertEqual(panel.pole, pole)
|
||||||
|
self.assertEqual(panel.street, self.adr_street)
|
||||||
|
self.assertIsInstance(panel.street, AdrStreet)
|
||||||
|
self.assertEqual(panel.vertical_order, 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
Image = None
|
Image = None
|
||||||
|
|
||||||
from common.models import UserConfig, UserThematics, Municipality
|
from common.models import UserConfig, UserThematics, Municipality, AdrStreet
|
||||||
from .models import (
|
from .models import (
|
||||||
SignPanelType,
|
SignPanelType,
|
||||||
SignStreet,
|
SignStreet,
|
||||||
|
|
@ -36,6 +36,103 @@ from .permissions import user_can_manage_regulations, user_can_edit_regulation,
|
||||||
from .forms import SignPoleForm, SignPanelForm
|
from .forms import SignPoleForm, SignPanelForm
|
||||||
|
|
||||||
|
|
||||||
|
def get_closest_adr_street(lat=None, lon=None, point=None, sign_street=None):
|
||||||
|
"""
|
||||||
|
Resolve corresponding common.AdrStreet from coordinates, Point, or SignStreet.
|
||||||
|
1. If sign_street is provided or can be found via spatial proximity, match by name or geom.
|
||||||
|
2. Fallback to point geometry directly on AdrStreet.
|
||||||
|
"""
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
from django.contrib.gis.db.models.functions import Distance
|
||||||
|
|
||||||
|
# 1. If sign_street is not provided, try to find the closest SignStreet spatially
|
||||||
|
if sign_street is None and (point is not None or (lat is not None and lon is not None)):
|
||||||
|
if point is None:
|
||||||
|
point = Point(float(lon), float(lat), srid=4326)
|
||||||
|
point_3812 = point.clone()
|
||||||
|
if point_3812.srid != 3812:
|
||||||
|
point_3812.transform(3812)
|
||||||
|
try:
|
||||||
|
sign_street = (
|
||||||
|
SignStreet.objects.filter(geom__isnull=False)
|
||||||
|
.annotate(distance=Distance('geom', point_3812))
|
||||||
|
.order_by('distance')
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
sign_street = None
|
||||||
|
|
||||||
|
if sign_street:
|
||||||
|
# Match by French name
|
||||||
|
if sign_street.name_fr:
|
||||||
|
qs = AdrStreet.objects.filter(name_fr__iexact=sign_street.name_fr.strip())
|
||||||
|
if sign_street.municipality_id:
|
||||||
|
adr = qs.filter(municipality_id=sign_street.municipality_id).first()
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
adr = qs.first()
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
|
||||||
|
# Match by Dutch name
|
||||||
|
if sign_street.name_nl:
|
||||||
|
qs = AdrStreet.objects.filter(name_nl__iexact=sign_street.name_nl.strip())
|
||||||
|
if sign_street.municipality_id:
|
||||||
|
adr = qs.filter(municipality_id=sign_street.municipality_id).first()
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
adr = qs.first()
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
|
||||||
|
# Match by proximity between SignStreet geom and AdrStreet geom
|
||||||
|
if sign_street.geom:
|
||||||
|
try:
|
||||||
|
adr = (
|
||||||
|
AdrStreet.objects.filter(geom__isnull=False)
|
||||||
|
.annotate(distance=Distance('geom', sign_street.geom))
|
||||||
|
.order_by('distance')
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2. Fallback to point geometry directly on AdrStreet
|
||||||
|
if point is not None or (lat is not None and lon is not None):
|
||||||
|
if point is None:
|
||||||
|
point = Point(float(lon), float(lat), srid=4326)
|
||||||
|
point_3812 = point.clone()
|
||||||
|
if point_3812.srid != 3812:
|
||||||
|
point_3812.transform(3812)
|
||||||
|
try:
|
||||||
|
adr = (
|
||||||
|
AdrStreet.objects.filter(geom__isnull=False)
|
||||||
|
.annotate(distance=Distance('geom', point_3812))
|
||||||
|
.order_by('distance')
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
adr = (
|
||||||
|
AdrStreet.objects.filter(centroid__isnull=False)
|
||||||
|
.annotate(distance=Distance('centroid', point_3812))
|
||||||
|
.order_by('distance')
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if adr:
|
||||||
|
return adr
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_grouped_panel_types():
|
def get_grouped_panel_types():
|
||||||
panel_types = list(SignPanelType.objects.all().order_by('code'))
|
panel_types = list(SignPanelType.objects.all().order_by('code'))
|
||||||
|
|
||||||
|
|
@ -2096,7 +2193,6 @@ def regulation_abrogation_article_voiries_suggest(request, regulation_id: int, a
|
||||||
@login_required
|
@login_required
|
||||||
def sign_pole_create(request):
|
def sign_pole_create(request):
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
from django.contrib.gis.db.models.functions import Distance
|
|
||||||
|
|
||||||
user_config = get_object_or_404(UserConfig, user=request.user)
|
user_config = get_object_or_404(UserConfig, user=request.user)
|
||||||
if not user_config.can_access_view('sign') or not user_can_edit_esign_assets(request.user):
|
if not user_config.can_access_view('sign') or not user_can_edit_esign_assets(request.user):
|
||||||
|
|
@ -2111,29 +2207,20 @@ def sign_pole_create(request):
|
||||||
lat = form.cleaned_data.get('lat')
|
lat = form.cleaned_data.get('lat')
|
||||||
lon = form.cleaned_data.get('lon')
|
lon = form.cleaned_data.get('lon')
|
||||||
|
|
||||||
# But let's ensure it's correct with explicit float conversion
|
# Ensure correct coordinates and projection (Lambert 2008: SRID 3812)
|
||||||
if lat and lon:
|
if lat and lon:
|
||||||
pole.lon = float(lon)
|
pole.lon = float(lon)
|
||||||
pole.lat = float(lat)
|
pole.lat = float(lat)
|
||||||
pole.geom = Point(float(lon), float(lat), srid=4326)
|
pole.geom = Point(float(lon), float(lat), srid=4326)
|
||||||
pole.geojson = pole.geom.geojson
|
pole.geojson = pole.geom.geojson
|
||||||
pole.geom.transform(3852)
|
pole.geom.transform(3812)
|
||||||
|
|
||||||
# If street is not provided, find the closest street
|
# If street is not provided, find the closest AdrStreet
|
||||||
if not pole.street_id:
|
if not pole.street_id and lat and lon:
|
||||||
if lat and lon:
|
adr_street = get_closest_adr_street(lat=lat, lon=lon)
|
||||||
pole_point = Point(float(lon), float(lat), srid=4326)
|
if adr_street:
|
||||||
closest_street = SignStreet.objects.annotate(
|
pole.street = adr_street
|
||||||
distance=Distance('geom', pole_point)
|
|
||||||
).order_by('distance').first()
|
|
||||||
if closest_street:
|
|
||||||
pole.street = closest_street
|
|
||||||
else:
|
|
||||||
return JsonResponse({'success': False, 'errors': {'street': [_('Aucune rue trouvée à proximité.')]}}, status=400)
|
|
||||||
else:
|
|
||||||
return JsonResponse({'success': False, 'errors': {'geom': [_('Les coordonnées sont requises.')]}}, status=400)
|
|
||||||
|
|
||||||
# Now save for the FIRST time (commit=True)
|
|
||||||
pole.save()
|
pole.save()
|
||||||
return JsonResponse({'success': True, 'id': pole.id, 'message': _('Poteau créé avec succès.')})
|
return JsonResponse({'success': True, 'id': pole.id, 'message': _('Poteau créé avec succès.')})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -2174,7 +2261,11 @@ def sign_pole_edit(request, pole_id):
|
||||||
pole.lat = float(lat)
|
pole.lat = float(lat)
|
||||||
pole.geom = Point(float(lon), float(lat), srid=4326)
|
pole.geom = Point(float(lon), float(lat), srid=4326)
|
||||||
pole.geojson = pole.geom.geojson
|
pole.geojson = pole.geom.geojson
|
||||||
pole.geom.transform(3852)
|
pole.geom.transform(3812)
|
||||||
|
if not pole.street_id:
|
||||||
|
adr_street = get_closest_adr_street(lat=lat, lon=lon)
|
||||||
|
if adr_street:
|
||||||
|
pole.street = adr_street
|
||||||
|
|
||||||
pole.save()
|
pole.save()
|
||||||
return JsonResponse({'success': True, 'id': pole.id, 'message': _('Poteau modifié avec succès.')})
|
return JsonResponse({'success': True, 'id': pole.id, 'message': _('Poteau modifié avec succès.')})
|
||||||
|
|
@ -2205,7 +2296,6 @@ def sign_pole_edit(request, pole_id):
|
||||||
@login_required
|
@login_required
|
||||||
def sign_panel_create(request):
|
def sign_panel_create(request):
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
from django.contrib.gis.db.models.functions import Distance
|
|
||||||
|
|
||||||
user_config = get_object_or_404(UserConfig, user=request.user)
|
user_config = get_object_or_404(UserConfig, user=request.user)
|
||||||
if not user_config.can_access_view('sign') or not user_can_edit_esign_assets(request.user):
|
if not user_config.can_access_view('sign') or not user_can_edit_esign_assets(request.user):
|
||||||
|
|
@ -2214,31 +2304,51 @@ def sign_panel_create(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = SignPanelForm(request.POST)
|
form = SignPanelForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
panel = form.save(commit=False)
|
try:
|
||||||
lat = form.cleaned_data.get('lat')
|
panel = form.save(commit=False)
|
||||||
lon = form.cleaned_data.get('lon')
|
lat = form.cleaned_data.get('lat')
|
||||||
|
lon = form.cleaned_data.get('lon')
|
||||||
# If pole_id is provided, link it and inherit its street
|
|
||||||
pole_id = request.POST.get('pole_id')
|
# If pole_id is provided, link it and inherit its street and geom
|
||||||
if pole_id:
|
pole_id = request.POST.get('pole_id')
|
||||||
try:
|
if pole_id:
|
||||||
pole = SignPole.objects.get(id=pole_id)
|
try:
|
||||||
panel.pole = pole
|
pole = SignPole.objects.get(id=pole_id)
|
||||||
panel.street = pole.street
|
panel.pole = pole
|
||||||
except SignPole.DoesNotExist:
|
panel.street = pole.street
|
||||||
pass
|
if pole.geom:
|
||||||
|
panel.geom = pole.geom
|
||||||
# If street is still not set and we have coordinates, find the closest street
|
panel.lon = pole.lon
|
||||||
if lat and lon and not panel.street_id:
|
panel.lat = pole.lat
|
||||||
panel_point = Point(lon, lat, srid=4326)
|
panel.geojson = pole.geojson
|
||||||
closest_street = SignStreet.objects.annotate(
|
except SignPole.DoesNotExist:
|
||||||
distance=Distance('geom', panel_point)
|
pass
|
||||||
).order_by('distance').first()
|
|
||||||
if closest_street:
|
# If coordinates are provided and geom not yet set
|
||||||
panel.street = closest_street
|
if lat and lon and not panel.geom:
|
||||||
|
panel.lon = float(lon)
|
||||||
panel.save()
|
panel.lat = float(lat)
|
||||||
return JsonResponse({'success': True, 'id': panel.id, 'message': _('Panneau créé avec succès.')})
|
panel.geom = Point(float(lon), float(lat), srid=4326)
|
||||||
|
panel.geojson = panel.geom.geojson
|
||||||
|
panel.geom.transform(3812)
|
||||||
|
|
||||||
|
# If street is still not set and we have coordinates, find the closest AdrStreet
|
||||||
|
if not panel.street_id and lat and lon:
|
||||||
|
adr_street = get_closest_adr_street(lat=lat, lon=lon)
|
||||||
|
if adr_street:
|
||||||
|
panel.street = adr_street
|
||||||
|
|
||||||
|
# Set vertical order if attached to a pole
|
||||||
|
if panel.pole and panel.vertical_order is None:
|
||||||
|
max_order = panel.pole.sign_app_panels.aggregate(m=Max('vertical_order'))['m'] or 0
|
||||||
|
panel.vertical_order = max_order + 1
|
||||||
|
|
||||||
|
panel.save()
|
||||||
|
return JsonResponse({'success': True, 'id': panel.id, 'message': _('Panneau créé avec succès.')})
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return JsonResponse({'success': False, 'errors': {'__all__': [str(e)]}}, status=500)
|
||||||
else:
|
else:
|
||||||
return JsonResponse({'success': False, 'errors': form.errors}, status=400)
|
return JsonResponse({'success': False, 'errors': form.errors}, status=400)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue