Skip to main content

Les statistiques Sitadel

Pour documenter cette procédure, il est crucial de noter que nous avons abandonné les outils classiques comme ogr2ogr (trop sensibles aux noms de colonnes longs de Sitadel) pour une méthode hybride Python + SQL.

Cette approche est la seule qui garantit 100% de succès sur des fichiers de plus de 100 000 lignes avec des en-têtes complexes.


📑 Procédure d'import Sitadel (CSV vers PostGIS)

1. Préparation de la structure SQL

Avant d'importer, on crée une table "cible" avec des noms de colonnes courts et explicites. Cela évite que PostgreSQL ne tronque les noms originaux (ex: "surface de plancher de la destination...") et ne crée des doublons.

Bash
# Connexion à la base et création de la table
docker exec -i alteris_postgis psql -U alteris_admin -d alteris_geo -c "
DROP TABLE IF EXISTS sitadel_2026_04.logements_chantiers;
CREATE TABLE sitadel_2026_04.logements_chantiers (
    num_permis text,    -- Identifiant DAU
    code_insee text,    -- Code commune
    date_chantier text, -- Date DOC
    nb_log text,        -- Nombre total logements
    nb_social text,     -- Logements locatifs sociaux
    promoteur text,     -- Dénomination Personne Morale
    adresse_num text,   -- Numéro de voie
    adresse_voie text,  -- Libellé de voie
    parcelle text       -- Numéro parcelle cadastrale
);"


2. Le Script d'import "Sniper" (Python)

Plutôt que d'envoyer tout le fichier (200+ colonnes), ce script sélectionne uniquement les colonnes légales nécessaires. Il gère également le séparateur (virgule , ou point-virgule ;) et nettoie les données à la volée.

Code à exécuter dans le terminal du VPS :

Python
python3 -c "
import csv, sys

# Mapping entre les en-têtes officiels Sitadel et nos colonnes SQL
mapping = {
    'Numéro d’enregistrement de la DAU': 'num_permis',
    'Code de la commune du lieu des travaux': 'code_insee',
    'Date réelle d’ouverture de chantier': 'date_chantier',
    'Nombre total de logements créés': 'nb_log',
    'Nb de logements locatifs sociaux': 'nb_social',
    'Dénomination d’un demandeur avéré en tant que personne morale': 'promoteur',
    'Numéro de voie du terrain': 'adresse_num',
    'Libellé de la voie du terrain': 'adresse_voie',
    'Numéro parcelle cadastrale 1': 'parcelle'
}

with open('2604_SitadelR93_logements.csv', 'r', encoding='utf-8-sig') as f:
    # Lecture avec séparateur virgule (export QGIS standard)
    reader = csv.reader(f, delimiter=',')
    header = next(reader)
    
    # Nettoyage des en-têtes (suppression des guillemets et espaces)
    header = [h.strip().replace('\"', '') for h in header]
    
    # Identification des index de colonnes
    indices = [header.index(k) if k in header else None for k in mapping.keys()]

    # Sortie formatée pour PostgreSQL COPY
    writer = csv.writer(sys.stdout, delimiter='|')
    for row in reader:
        if len(row) > 0:
            # On écrit uniquement les colonnes mappées
            writer.writerow([row[i] if i is not None and i < len(row) else '' for i in indices])
" | docker exec -i alteris_postgis psql -U alteris_admin -d alteris_geo \
-c "COPY sitadel_2026_04.logements_chantiers FROM STDIN WITH (FORMAT csv, DELIMITER '|');"


3. Pourquoi cette méthode est la meilleure ?

Caractéristique Avantage
Vitesse Le moteur COPY de PostgreSQL importe 120 000 lignes en moins de 3 secondes.
Fiabilité En utilisant un délimiteur personnalisé (`
Légèreté On n'occupe pas d'espace disque inutile en ignorant les 190 colonnes techniques non utilisées.
RGPD On peut filtrer ou anonymiser les données sensibles avant qu'elles n'atteignent la base de données.

🛠️ Vérification de l'import

Pour valider que la procédure a réussi, on compte les lignes chargées :

Bash
docker exec -i alteris_postgis psql -U alteris_admin -d alteris_geo -c "SELECT count(*) FROM sitadel_2026_04.logements_chantiers;"

2/ les extractions

On enchaîne. Une fois que la donnée est "au chaud" dans PostGIS, l'objectif est d'extraire l'intelligence métier. Pour ton rapport, on ne veut pas de fioritures : on veut de la donnée légale, datée et localisée.

Voici la documentation des requêtes SQL pour extraire le bilan des chantiers engagés à Bandol.


📑 Procédure d'Analyse SQL (Indicateurs C5e)

1. Bilan Annuel des Chantiers Réels

Cette requête groupe les données par année en se basant sur la Date de Déclaration d'Ouverture de Chantier (DOC). C'est l'indicateur de santé réelle de la construction.

SQL
-- Calcul du flux annuel de logements engagés
SELECT 
    LEFT(date_chantier, 4) as annee, 
    COUNT(*) as nb_permis_doc,
    SUM(nb_log::int) as total_logements,
    SUM(COALESCE(nb_social, '0')::int) as dont_social,
    ROUND((SUM(COALESCE(nb_social, '0')::float) / NULLIF(SUM(nb_log::float), 0) * 100)::numeric, 1) as part_social_pct
FROM sitadel_2026_04.logements_chantiers 
WHERE code_insee = '83009' -- Filtre Bandol
  AND date_chantier >= '2019-01-01'
GROUP BY annee
ORDER BY annee DESC;


2. Identification des 10 Projets Majeurs

Cette requête permet d'isoler les opérations qui structurent le marché local. On y intègre l'identifiant DAU (Numéro de permis) pour assurer la traçabilité juridique.

SQL
-- Listing des 10 plus gros chantiers depuis 2019
SELECT 
    num_permis as dau_officiel,
    COALESCE(NULLIF(promoteur, ''), 'PARTICULIER') as operateur, 
    nb_log::int as nb_log,
    COALESCE(nb_social, '0')::int as nb_social,
    date_chantier as date_doc,
    CONCAT(adresse_num, ' ', adresse_voie) as adresse_chantier,
    parcelle as ref_cadastrale
FROM sitadel_2026_04.logements_chantiers 
WHERE code_insee = '83009' 
  AND date_chantier >= '2019-01-01'
ORDER BY nb_log::int DESC
LIMIT 10;


3. Les points de vigilance technique (pour ta doc)

  • Le Cast Type (::int) : Comme Sitadel contient parfois des cases vides ou des caractères bizarres, nous avons importé les colonnes en TEXT. Pour faire des calculs (SUM, ORDER BY), il est impératif de forcer le type en entier avec ::int ou ::float.

  • Le COALESCE : Indispensable pour la colonne Social. Si la case est vide, PostgreSQL renvoie NULL. Le COALESCE(..., '0') force la valeur à zéro pour ne pas fausser les additions.

  • Le NULLIF : Prévient l'erreur "division par zéro" dans le calcul du pourcentage si une année ne compte aucun logement.


📦 4. Export vers le format GeoPackage (GPKG)

Pour que ton équipe SIG puisse utiliser ces données sans se reconnecter à la base, on documente la commande d'exportation vers un fichier portable.

Bash
# Export de la table filtrée Bandol vers un fichier GPKG
docker exec -i alteris_postgis ogr2ogr -f GPKG /tmp/bandol_bilan_chantiers.gpkg \
  PG:"host=localhost dbname=alteris_geo user=alteris_admin" \
  -sql "SELECT * FROM sitadel_2026_04.logements_chantiers WHERE code_insee = '83009' AND date_chantier >= '2019-01-01'" \
  -nln sitadel_chantiers_engages

# Récupération du fichier sur le stockage local du VPS
docker cp alteris_postgis:/tmp/bandol_bilan_chantiers.gpkg ./bandol_bilan_chantiers_2019_2026.gpkg


C'est la fin du volet "Données & Requêtes". Veux-tu qu'on termine cette documentation par la partie Cartographique : comment faire la jointure dans QGIS entre ce fichier GPKG et tes parcelles cadastrales pour obtenir la carte finale des chantiers ?