Merge branch 'main' of https://git.valogeo.be/Valogeo/loko
This commit is contained in:
commit
4823d55f08
5 changed files with 506 additions and 10 deletions
108
loko/assets/migrations/0031_naturetree_amenity_value_and_more.py
Normal file
108
loko/assets/migrations/0031_naturetree_amenity_value_and_more.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# Generated by Django 6.0.7 on 2026-10-07 15:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0030_managementzone_managementzonedocument_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='amenity_value',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name="Valeur d'aménité (€)"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='biodiversity_index',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Indice de biodiversité'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='carbon_stock',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Stock de carbone (t CO2)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='cooling_energy_indicator',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Énergie de refroidissement (kWh)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='cooling_indicator',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Indice de rafraîchissement'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='cultivar',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Cultivar / Variété'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='death_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='Date de dépérissement / mort'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='genus',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Genre botanique'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='is_chronoxyle',
|
||||
field=models.BooleanField(default=False, verbose_name='Arbre sénescent / Chronoxyle (bois mort sur pied)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='is_dead',
|
||||
field=models.BooleanField(default=False, verbose_name='Arbre mort'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='is_stump',
|
||||
field=models.BooleanField(default=False, verbose_name='Souche'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='management_type',
|
||||
field=models.CharField(blank=True, choices=[('municipal', 'Communal (Régie communale)'), ('regional', 'Régional (Bruxelles Mobilité / Région)'), ('community', 'Intercommunal / Autre pouvoir public'), ('private', 'Privé / Tiers')], default='municipal', max_length=50, null=True, verbose_name='Type de gestionnaire'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='managing_authority',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Autorité gestionnaire'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='number_of_trunks',
|
||||
field=models.IntegerField(blank=True, default=1, null=True, verbose_name='Nombre de troncs / Cépée'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='plantation_coefficient',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Coefficient de plantation'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='planting_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='Date de plantation réelle'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='scientific_name',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Nom scientifique / latin'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='situation_coefficient',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='Coefficient de situation'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='naturetree',
|
||||
name='vernacular_name',
|
||||
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Nom vernaculaire / Essence'),
|
||||
),
|
||||
]
|
||||
|
|
@ -146,6 +146,13 @@ TREE_PRUNING_CHOICES = [
|
|||
('none', _("Non taillé / Entretien minimal")),
|
||||
]
|
||||
|
||||
TREE_MANAGEMENT_TYPE_CHOICES = [
|
||||
('municipal', _("Communal (Régie communale)")),
|
||||
('regional', _("Régional (Bruxelles Mobilité / Région)")),
|
||||
('community', _("Intercommunal / Autre pouvoir public")),
|
||||
('private', _("Privé / Tiers")),
|
||||
]
|
||||
|
||||
|
||||
BULB_TYPE_CHOICES = [
|
||||
('tulipes', _('Tulipes')),
|
||||
|
|
@ -224,6 +231,37 @@ class NatureTree(AbstractGeoAsset):
|
|||
pruning_frequency_years = models.IntegerField(null=True, blank=True, verbose_name=_("Périodicité de taille (années)"))
|
||||
last_pruning_date = models.DateField(null=True, blank=True, verbose_name=_("Dernière taille / élagage"))
|
||||
|
||||
# Gestionnaire & Compétence
|
||||
managing_authority = models.CharField(max_length=200, null=True, blank=True, verbose_name=_("Autorité gestionnaire"))
|
||||
management_type = models.CharField(max_length=50, choices=TREE_MANAGEMENT_TYPE_CHOICES, default='municipal', null=True, blank=True, verbose_name=_("Type de gestionnaire"))
|
||||
|
||||
# Taxonomie & Botanique
|
||||
vernacular_name = models.CharField(max_length=200, null=True, blank=True, verbose_name=_("Nom vernaculaire / Essence"))
|
||||
scientific_name = models.CharField(max_length=200, null=True, blank=True, verbose_name=_("Nom scientifique / latin"))
|
||||
genus = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Genre botanique"))
|
||||
cultivar = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Cultivar / Variété"))
|
||||
|
||||
# Dendrométrie & Morphologie complémentaire
|
||||
number_of_trunks = models.IntegerField(null=True, blank=True, default=1, verbose_name=_("Nombre de troncs / Cépée"))
|
||||
|
||||
# Éco-services & Valeur patrimoniale (Indicateurs écosystémiques)
|
||||
amenity_value = models.FloatField(null=True, blank=True, verbose_name=_("Valeur d'aménité (€)"))
|
||||
carbon_stock = models.FloatField(null=True, blank=True, verbose_name=_("Stock de carbone (t CO2)"))
|
||||
cooling_indicator = models.FloatField(null=True, blank=True, verbose_name=_("Indice de rafraîchissement"))
|
||||
cooling_energy_indicator = models.FloatField(null=True, blank=True, verbose_name=_("Énergie de refroidissement (kWh)"))
|
||||
biodiversity_index = models.FloatField(null=True, blank=True, verbose_name=_("Indice de biodiversité"))
|
||||
|
||||
# Évaluation patrimoniale & Barème
|
||||
situation_coefficient = models.FloatField(null=True, blank=True, verbose_name=_("Coefficient de situation"))
|
||||
plantation_coefficient = models.FloatField(null=True, blank=True, verbose_name=_("Coefficient de plantation"))
|
||||
|
||||
# Statut biologique particulier
|
||||
is_dead = models.BooleanField(default=False, verbose_name=_("Arbre mort"))
|
||||
is_stump = models.BooleanField(default=False, verbose_name=_("Souche"))
|
||||
is_chronoxyle = models.BooleanField(default=False, verbose_name=_("Arbre sénescent / Chronoxyle (bois mort sur pied)"))
|
||||
death_date = models.DateField(null=True, blank=True, verbose_name=_("Date de dépérissement / mort"))
|
||||
planting_date = models.DateField(null=True, blank=True, verbose_name=_("Date de plantation réelle"))
|
||||
|
||||
# Références des choix pour utilisation simple dans les templates
|
||||
DEVELOPMENT_STAGE_CHOICES = TREE_DEVELOPMENT_STAGE_CHOICES
|
||||
VITALITY_CHOICES = TREE_VITALITY_CHOICES
|
||||
|
|
@ -233,6 +271,7 @@ class NatureTree(AbstractGeoAsset):
|
|||
PIT_TYPE_CHOICES = TREE_PIT_TYPE_CHOICES
|
||||
PROTECTION_CHOICES = TREE_PROTECTION_CHOICES
|
||||
PRUNING_CHOICES = TREE_PRUNING_CHOICES
|
||||
MANAGEMENT_TYPE_CHOICES = TREE_MANAGEMENT_TYPE_CHOICES
|
||||
|
||||
indexes = [
|
||||
models.Index(fields=["code"]),
|
||||
|
|
|
|||
|
|
@ -64,6 +64,16 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="management_type" class="form-label">{% translate "Gestionnaire / Compétence" %}</label>
|
||||
<select name="management_type" id="management_type" class="form-select" multiple>
|
||||
{% for key, value in management_type_choices %}
|
||||
<option value="{{ key }}" {% if key in selected_management_types %}selected{% endif %}>
|
||||
{{ value }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 d-flex justify-content-between mt-4" style="width: 200px;">
|
||||
<button type="button" id="apply-assets-filters" class="btn btn-sm btn-primary">{% translate "Filtrer" %}</button>
|
||||
<button type="button" id="reset-assets-filters" class="btn btn-sm btn-secondary">{% translate "Réinitialiser" %}</button>
|
||||
|
|
@ -86,12 +96,15 @@
|
|||
{ field: 'highway', title: '{% translate "Ring/Autoroute" %}', align: 'center' }
|
||||
];
|
||||
|
||||
var assets_property_fields = ['category', 'model', 'area'];
|
||||
var assets_property_fields = ['code', 'name', 'managing_authority', 'model', 'scientific_name', 'area'];
|
||||
var assets_columns = [
|
||||
{ field: 'category', title: '{% translate "Catégorie" %}' },
|
||||
{ field: 'code', title: '{% translate "Code" %}' },
|
||||
{ field: 'name', title: '{% translate "Nom" %}' },
|
||||
{ field: 'managing_authority', title: '{% translate "Gestionnaire" %}' },
|
||||
{ field: 'model', title: '{% translate "Modèle" %}' },
|
||||
{ field: 'scientific_name', title: '{% translate "Botanique" %}' },
|
||||
{ field: 'area', title: '{% translate "Superficie" %}' }
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
const locations_layer_def = {
|
||||
|
|
@ -161,16 +174,25 @@
|
|||
16, 5,
|
||||
20, 8
|
||||
],
|
||||
'circle-color': ['coalesce', ['get', 'color'], '#0277BD'],
|
||||
'circle-color': [
|
||||
'case',
|
||||
['==', ['get', 'management_type'], 'regional'], '#1E3A8A',
|
||||
['boolean', ['get', 'is_remarkable'], false], '#F59E0B',
|
||||
['coalesce', ['get', 'color'], '#15803D']
|
||||
],
|
||||
'circle-stroke-width': [
|
||||
'case',
|
||||
['boolean', ['feature-state', 'highlighted'], false], 2,
|
||||
['boolean', ['feature-state', 'highlighted'], false], 2.5,
|
||||
['==', ['get', 'management_type'], 'regional'], 1.5,
|
||||
['boolean', ['get', 'is_remarkable'], false], 2,
|
||||
1
|
||||
],
|
||||
'circle-stroke-color': [
|
||||
'case',
|
||||
['boolean', ['feature-state', 'highlighted'], false], '#ff0000',
|
||||
'#000000'
|
||||
['boolean', ['feature-state', 'highlighted'], false], '#EF4444',
|
||||
['==', ['get', 'management_type'], 'regional'], '#93C5FD',
|
||||
['boolean', ['get', 'is_remarkable'], false], '#FEF08A',
|
||||
'#064E3B'
|
||||
],
|
||||
'circle-opacity': 0.8
|
||||
},
|
||||
|
|
|
|||
|
|
@ -88,6 +88,24 @@
|
|||
{% if asset.is_remarkable %}
|
||||
<span class="badge bg-warning text-dark ms-1 shadow-sm"><i class="bi bi-star-fill text-warning me-1"></i>{% translate "Arbre remarquable" %}</span>
|
||||
{% endif %}
|
||||
|
||||
{# Badge Gestionnaire #}
|
||||
{% if asset.managing_authority %}
|
||||
<span class="badge {% if asset.management_type == 'regional' %}bg-primary-subtle text-primary border border-primary{% else %}bg-success-subtle text-success border border-success{% endif %} ms-1">
|
||||
<i class="bi bi-buildings me-1"></i>{{ asset.managing_authority }}
|
||||
</span>
|
||||
{% elif asset.management_type %}
|
||||
<span class="badge bg-secondary-subtle text-secondary border ms-1">{{ asset.get_management_type_display }}</span>
|
||||
{% endif %}
|
||||
|
||||
{# Badges Biologiques Spéciaux #}
|
||||
{% if asset.is_dead %}
|
||||
<span class="badge bg-dark text-white ms-1"><i class="bi bi-x-circle me-1"></i>{% translate "Mort" %}</span>
|
||||
{% elif asset.is_stump %}
|
||||
<span class="badge bg-secondary text-white ms-1"><i class="bi bi-dash-circle me-1"></i>{% translate "Souche" %}</span>
|
||||
{% elif asset.is_chronoxyle %}
|
||||
<span class="badge bg-warning-subtle text-dark border border-warning ms-1"><i class="bi bi-recycle me-1"></i>{% translate "Chronoxyle" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mt-1" style="font-size: 0.8rem; color: #6c757d;">
|
||||
|
|
@ -159,6 +177,24 @@
|
|||
{% if asset.is_remarkable %}
|
||||
<span class="badge bg-warning text-dark ms-1 shadow-sm"><i class="bi bi-star-fill text-warning me-1"></i>{% translate "Arbre remarquable" %}</span>
|
||||
{% endif %}
|
||||
|
||||
{# Badge Gestionnaire #}
|
||||
{% if asset.managing_authority %}
|
||||
<span class="badge {% if asset.management_type == 'regional' %}bg-primary-subtle text-primary border border-primary{% else %}bg-success-subtle text-success border border-success{% endif %} ms-1">
|
||||
<i class="bi bi-buildings me-1"></i>{{ asset.managing_authority }}
|
||||
</span>
|
||||
{% elif asset.management_type %}
|
||||
<span class="badge bg-secondary-subtle text-secondary border ms-1">{{ asset.get_management_type_display }}</span>
|
||||
{% endif %}
|
||||
|
||||
{# Badges Biologiques Spéciaux #}
|
||||
{% if asset.is_dead %}
|
||||
<span class="badge bg-dark text-white ms-1"><i class="bi bi-x-circle me-1"></i>{% translate "Mort" %}</span>
|
||||
{% elif asset.is_stump %}
|
||||
<span class="badge bg-secondary text-white ms-1"><i class="bi bi-dash-circle me-1"></i>{% translate "Souche" %}</span>
|
||||
{% elif asset.is_chronoxyle %}
|
||||
<span class="badge bg-warning-subtle text-dark border border-warning ms-1"><i class="bi bi-recycle me-1"></i>{% translate "Chronoxyle" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mt-1" style="font-size: 0.85rem; color: #6c757d;">
|
||||
|
|
@ -221,6 +257,89 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Autorité gestionnaire -->
|
||||
<tr class="asset-editable-field" data-field="managing_authority" data-can-edit="{% if 'managing_authority' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Autorité gestionnaire" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value fw-semibold text-primary">
|
||||
{% if asset.managing_authority %}
|
||||
<i class="bi bi-bank me-1"></i>{{ asset.managing_authority }}
|
||||
{% if asset.management_type %}
|
||||
<small class="text-muted fw-normal">({{ asset.get_management_type_display }})</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ asset.get_management_type_display|default:"-" }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ asset.managing_authority|default:'' }}" placeholder="{% translate 'Ex: Commune de Watermael-Boitsfort' %}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Type de gestionnaire -->
|
||||
<tr class="asset-editable-field" data-field="management_type" data-can-edit="{% if 'management_type' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Type de gestionnaire" %}</th>
|
||||
<td>
|
||||
<div class="field-display"><span class="field-value">{{ asset.get_management_type_display|default:"-" }}</span></div>
|
||||
<div class="field-edit d-none">
|
||||
<select class="form-select form-select-sm">
|
||||
{% for val, lbl in asset.TREE_MANAGEMENT_TYPE_CHOICES %}
|
||||
<option value="{{ val }}" {% if asset.management_type == val %}selected{% endif %}>{{ lbl }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Nom scientifique / Latin -->
|
||||
<tr class="asset-editable-field" data-field="scientific_name" data-can-edit="{% if 'scientific_name' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Nom scientifique / Latin" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value fst-italic fw-medium text-success">
|
||||
{{ asset.scientific_name|default:"-" }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ asset.scientific_name|default:'' }}" placeholder="{% translate 'Ex: Prunus serrulata' %}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Nom vernaculaire / Essence -->
|
||||
<tr class="asset-editable-field" data-field="vernacular_name" data-can-edit="{% if 'vernacular_name' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Nom vernaculaire / Essence" %}</th>
|
||||
<td>
|
||||
<div class="field-display"><span class="field-value">{{ asset.vernacular_name|default:"-" }}</span></div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ asset.vernacular_name|default:'' }}" placeholder="{% translate 'Ex: Cerisier du Japon' %}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Genre & Cultivar -->
|
||||
{% if asset.genus or asset.cultivar or 'genus' in allowed_update_fields %}
|
||||
<tr class="asset-editable-field" data-field="genus" data-can-edit="{% if 'genus' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Genre & Cultivar" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value">
|
||||
{{ asset.genus|default:"-" }}{% if asset.cultivar %} '{{ asset.cultivar }}'{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<div class="row g-1">
|
||||
<div class="col-6"><input type="text" class="form-control form-control-sm" placeholder="Genre" value="{{ asset.genus|default:'' }}"></div>
|
||||
<div class="col-6"><input type="text" class="form-control form-control-sm" placeholder="Cultivar" value="{{ asset.cultivar|default:'' }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<!-- Nom commun FR -->
|
||||
<tr class="asset-editable-field" data-field="name_fr" data-can-edit="{% if 'name_fr' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Nom commun (FR)" %}</th>
|
||||
|
|
@ -419,6 +538,25 @@
|
|||
|
||||
<table class="table table-striped mb-0 align-middle">
|
||||
<tbody>
|
||||
<!-- Nombre de troncs / Cépée -->
|
||||
<tr class="asset-editable-field" data-field="number_of_trunks" data-can-edit="{% if 'number_of_trunks' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th style="width: 35%;">{% translate "Nombre de troncs (cépée)" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value">
|
||||
{% if asset.number_of_trunks and asset.number_of_trunks > 1 %}
|
||||
<span class="badge bg-secondary-subtle text-secondary border"><i class="bi bi-diagram-3 me-1"></i>{{ asset.number_of_trunks }} {% translate "troncs" %}</span>
|
||||
{% else %}
|
||||
1 {% translate "(tronc unique)" %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="number" step="1" min="1" class="form-control form-control-sm" value="{{ asset.number_of_trunks|default:1 }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Hauteur totale -->
|
||||
<tr class="asset-editable-field" data-field="height" data-can-edit="{% if 'height' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th style="width: 35%;">{% translate "Hauteur totale (m)" %}</th>
|
||||
|
|
@ -518,6 +656,173 @@
|
|||
</div>
|
||||
|
||||
|
||||
{# ── CARTE ÉCO-SERVICES & VALEUR PATRIMONIALE (GRALITY / BARÈME DE L'ARBRE) ── #}
|
||||
<div class="card mb-3 shadow-sm border-success-subtle">
|
||||
<div class="card-header bg-success-subtle d-flex align-items-center justify-content-between">
|
||||
<h5 class="mb-0 fs-6 fw-bold text-success-emphasis">
|
||||
<i class="bi bi-feather me-2"></i>{% translate "Éco-services & Valeur patrimoniale" %}
|
||||
</h5>
|
||||
<span class="badge bg-success text-white">
|
||||
<i class="bi bi-shield-check me-1"></i>{% translate "Indicateurs écologiques" %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{# 4 mini-widgets écologiques #}
|
||||
<div class="row g-2 mb-3">
|
||||
<!-- Valeur d'aménité -->
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="p-2 border border-success-subtle rounded text-center bg-white shadow-sm tree-metric-widget">
|
||||
<div class="small text-muted mb-1 metric-title text-truncate" title="{% translate "Valeur d'aménité calculée" %}">
|
||||
<i class="bi bi-currency-euro text-success me-1"></i>{% translate "Aménité" %}
|
||||
</div>
|
||||
<div class="fs-5 fw-bold text-success metric-value">
|
||||
{% if asset.amenity_value %}
|
||||
{{ asset.amenity_value|floatformat:0 }} <small class="text-muted fw-normal" style="font-size: 0.75rem;">€</small>
|
||||
{% else %}
|
||||
<span class="text-muted fs-6">—</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stock carbone -->
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="p-2 border border-primary-subtle rounded text-center bg-white shadow-sm tree-metric-widget">
|
||||
<div class="small text-muted mb-1 metric-title text-truncate" title="{% translate 'Stock de carbone séquestré' %}">
|
||||
<i class="bi bi-cloud-check text-primary me-1"></i>{% translate "Carbone" %}
|
||||
</div>
|
||||
<div class="fs-5 fw-bold text-primary metric-value">
|
||||
{% if asset.carbon_stock %}
|
||||
{{ asset.carbon_stock|floatformat:2 }} <small class="text-muted fw-normal" style="font-size: 0.75rem;">t CO₂</small>
|
||||
{% else %}
|
||||
<span class="text-muted fs-6">—</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rafraîchissement urbain -->
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="p-2 border border-info-subtle rounded text-center bg-white shadow-sm tree-metric-widget">
|
||||
<div class="small text-muted mb-1 metric-title text-truncate" title="{% translate 'Potentiel de rafraîchissement urbain' %}">
|
||||
<i class="bi bi-thermometer-snow text-info me-1"></i>{% translate "Fraîcheur" %}
|
||||
</div>
|
||||
<div class="fs-5 fw-bold text-info-emphasis metric-value">
|
||||
{% if asset.cooling_indicator %}
|
||||
{{ asset.cooling_indicator|floatformat:1 }} <small class="text-muted fw-normal" style="font-size: 0.75rem;">/ 10</small>
|
||||
{% else %}
|
||||
<span class="text-muted fs-6">—</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indice de biodiversité -->
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="p-2 border border-warning-subtle rounded text-center bg-white shadow-sm tree-metric-widget">
|
||||
<div class="small text-muted mb-1 metric-title text-truncate" title="{% translate "Indice d'accueil biodiversité" %}">
|
||||
<i class="bi bi-bug text-warning me-1"></i>{% translate "Biodiversité" %}
|
||||
</div>
|
||||
<div class="fs-5 fw-bold text-warning-emphasis metric-value">
|
||||
{% if asset.biodiversity_index %}
|
||||
{{ asset.biodiversity_index|floatformat:2 }}
|
||||
{% else %}
|
||||
<span class="text-muted fs-6">—</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Table détaillée des indicateurs écosystémiques #}
|
||||
<table class="table table-striped mb-0 align-middle">
|
||||
<tbody>
|
||||
<!-- Énergie de refroidissement -->
|
||||
<tr class="asset-editable-field" data-field="cooling_energy_indicator" data-can-edit="{% if 'cooling_energy_indicator' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th style="width: 35%;">{% translate "Énergie de refroidissement (kWh)" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value">
|
||||
{% if asset.cooling_energy_indicator %}
|
||||
{{ asset.cooling_energy_indicator|floatformat:1 }} kWh
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="number" step="0.1" class="form-control form-control-sm" value="{{ asset.cooling_energy_indicator|default:'' }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Coefficient de situation -->
|
||||
<tr class="asset-editable-field" data-field="situation_coefficient" data-can-edit="{% if 'situation_coefficient' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Coefficient de situation (barème)" %}</th>
|
||||
<td>
|
||||
<div class="field-display"><span class="field-value">{{ asset.situation_coefficient|default:"-" }}</span></div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="number" step="0.1" class="form-control form-control-sm" value="{{ asset.situation_coefficient|default:'' }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Coefficient de plantation -->
|
||||
<tr class="asset-editable-field" data-field="plantation_coefficient" data-can-edit="{% if 'plantation_coefficient' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Coefficient de plantation (barème)" %}</th>
|
||||
<td>
|
||||
<div class="field-display"><span class="field-value">{{ asset.plantation_coefficient|default:"-" }}</span></div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="number" step="0.1" class="form-control form-control-sm" value="{{ asset.plantation_coefficient|default:'' }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Date de plantation réelle -->
|
||||
<tr class="asset-editable-field" data-field="planting_date" data-can-edit="{% if 'planting_date' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Date de plantation constatée" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value">
|
||||
{% if asset.planting_date %}
|
||||
<i class="bi bi-calendar3 me-1 text-muted"></i>{{ asset.planting_date|date:"d/m/Y" }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="date" class="form-control form-control-sm" value="{{ asset.planting_date|date:'Y-m-d'|default:'' }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Date de dépérissement / mort -->
|
||||
{% if asset.death_date or asset.is_dead or 'death_date' in allowed_update_fields %}
|
||||
<tr class="asset-editable-field" data-field="death_date" data-can-edit="{% if 'death_date' in allowed_update_fields %}true{% else %}false{% endif %}">
|
||||
<th>{% translate "Date de mort / dépérissement" %}</th>
|
||||
<td>
|
||||
<div class="field-display">
|
||||
<span class="field-value text-danger">
|
||||
{% if asset.death_date %}
|
||||
<i class="bi bi-calendar-x me-1"></i>{{ asset.death_date|date:"d/m/Y" }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-edit d-none">
|
||||
<input type="date" class="form-control form-control-sm" value="{{ asset.death_date|date:'Y-m-d'|default:'' }}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{# ── CARTE 3 : DIAGNOSTIC SANITAIRE & SÉCURITÉ ── #}
|
||||
<div class="card mb-3 shadow-sm">
|
||||
<div class="card-header bg-light d-flex align-items-center justify-content-between">
|
||||
|
|
|
|||
|
|
@ -71,6 +71,14 @@ def nature_index(request):
|
|||
categories.sort(key=lambda m: m.get_name().lower())
|
||||
category_choices = [(m.id, m.get_name()) for m in categories]
|
||||
|
||||
selected_management_types = request.GET.getlist('management_type')
|
||||
management_type_choices = [
|
||||
('municipal', _("Communal (Commune)")),
|
||||
('regional', _("Régional (Bruxelles Mobilité)")),
|
||||
('community', _("Intercommunal / Communautaire")),
|
||||
('private', _("Privé / Conventionné")),
|
||||
]
|
||||
|
||||
models = list(NatureAssetModel.objects.all())
|
||||
models.sort(key=lambda m: (m.get_name() or "").lower())
|
||||
model_choices = [(m.id, m.get_name()) for m in models]
|
||||
|
|
@ -92,6 +100,8 @@ def nature_index(request):
|
|||
'model_choices': model_choices,
|
||||
'category_model_map': dict(category_model_map),
|
||||
'selected_thematic': selected_thematic,
|
||||
'management_type_choices': management_type_choices,
|
||||
'selected_management_types': selected_management_types,
|
||||
'selected_statuses': statuses,
|
||||
'selected_categories': selected_categories,
|
||||
'selected_models': selected_models,
|
||||
|
|
@ -551,7 +561,7 @@ def ensure_list(value):
|
|||
return [value]
|
||||
|
||||
|
||||
MAX_ASSET_FEATURES = 5000 # limite imposée
|
||||
MAX_ASSET_FEATURES = 10000 # limite imposée
|
||||
|
||||
@check_thematic_access('nature')
|
||||
def nature_assets_geojson(request):
|
||||
|
|
@ -570,6 +580,7 @@ def nature_assets_geojson(request):
|
|||
models = ensure_list(data.get('model'))
|
||||
highway = data.get('highway') == "1"
|
||||
location_type = data.get('location_type', '')
|
||||
management_types = ensure_list(data.get('management_type'))
|
||||
|
||||
# Récupération et parsing de la géométrie
|
||||
filter_geom_raw = data.get("filter_geom")
|
||||
|
|
@ -590,11 +601,12 @@ def nature_assets_geojson(request):
|
|||
models = request.GET.getlist('model', [])
|
||||
highway = request.GET.get('highway') == "1"
|
||||
location_type = request.GET.get('location_type', '')
|
||||
management_types = request.GET.getlist('management_type', [])
|
||||
filter_geom = None
|
||||
|
||||
|
||||
_search_q = (data.get('q') or '').strip() if request.method == 'POST' else ''
|
||||
if not request.GET.get('unified') and not sections_ids and not categories and not models and not filter_geom and not _search_q and not highway and not location_type: # return empty geojson if no param with section_id
|
||||
if not request.GET.get('unified') and not sections_ids and not categories and not models and not filter_geom and not _search_q and not highway and not location_type and not management_types: # return empty geojson if no param with section_id
|
||||
geojson_data = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [],
|
||||
|
|
@ -664,6 +676,11 @@ def nature_assets_geojson(request):
|
|||
"thematic": "nature",
|
||||
"is_location": False,
|
||||
"structure_type": obj.get('structure_type', ''),
|
||||
"managing_authority": obj.get('managing_authority') or '',
|
||||
"management_type": obj.get('management_type') or '',
|
||||
"is_remarkable": obj.get('is_remarkable', False),
|
||||
"scientific_name": obj.get('scientific_name') or '',
|
||||
"vernacular_name": obj.get('vernacular_name') or '',
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -715,6 +732,9 @@ def nature_assets_geojson(request):
|
|||
naturetree_qs = naturetree_qs.filter(model__id__in=models)
|
||||
natureshrub_qs = natureshrub_qs.filter(model__id__in=models)
|
||||
naturebench_qs = naturebench_qs.filter(model__id__in=models)
|
||||
|
||||
if management_types:
|
||||
naturetree_qs = naturetree_qs.filter(management_type__in=management_types)
|
||||
if categories:
|
||||
naturegreensurface_qs = naturegreensurface_qs.filter(category__id__in=categories)
|
||||
naturetree_qs = naturetree_qs.filter(category__id__in=categories)
|
||||
|
|
@ -790,7 +810,9 @@ def nature_assets_geojson(request):
|
|||
.values( # on limite strictement les colonnes
|
||||
'id', 'code', 'name', 'status', 'validation_status', 'lon', 'lat',
|
||||
'category_name', 'model_name', 'color', 'geojson',
|
||||
'category_id', 'model_id', 'location_lon', 'location_lat'
|
||||
'category_id', 'model_id', 'location_lon', 'location_lat',
|
||||
'managing_authority', 'management_type', 'is_remarkable',
|
||||
'scientific_name', 'vernacular_name'
|
||||
)
|
||||
.order_by('code')
|
||||
)[:MAX_ASSET_FEATURES + 1]
|
||||
|
|
|
|||
Loading…
Reference in a new issue