# 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.

<div class="code-block ng-tns-c2968650486-5619 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-bash"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2968650486-5619 ng-star-inserted"><span class="ng-tns-c2968650486-5619">Bash</span><div class="buttons ng-tns-c2968650486-5619 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2968650486-5619"><div class="animated-opacity ng-tns-c2968650486-5619"></div></div></div>```
# 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
);"

```

<div class="code-block ng-tns-c2968650486-5619 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--2"><div class="formatted-code-block-internal-container ng-tns-c2968650486-5619"><div class="animated-opacity ng-tns-c2968650486-5619"></div></div></div>---

### 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 :**

<div class="code-block ng-tns-c2968650486-5620 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-python"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2968650486-5620 ng-star-inserted"><span class="ng-tns-c2968650486-5620">Python</span><div class="buttons ng-tns-c2968650486-5620 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2968650486-5620"><div class="animated-opacity ng-tns-c2968650486-5620"></div></div></div>```
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 '|');"

```

<div class="code-block ng-tns-c2968650486-5620 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--6"><div class="formatted-code-block-internal-container ng-tns-c2968650486-5620"><div class="animated-opacity ng-tns-c2968650486-5620"></div></div></div>---

### 3. Pourquoi cette méthode est la meilleure ?

<div class="horizontal-scroll-wrapper" id="bkmrk-caract%C3%A9ristique-avan"><div class="table-block-component"><div class="table-block has-export-button new-table-style is-at-scroll-start is-at-scroll-end"><div class="table-content not-end-of-paragraph"><table><thead><tr><th><span>Caractéristique</span></th><th><span>Avantage</span></th></tr></thead><tbody><tr><td><span>**Vitesse**</span></td><td><span>Le moteur `COPY` de PostgreSQL importe 120 000 lignes en moins de 3 secondes.</span></td></tr><tr><td><span>**Fiabilité**</span></td><td><span>En utilisant un délimiteur personnalisé (`</span></td></tr><tr><td><span>**Légèreté**</span></td><td><span>On n'occupe pas d'espace disque inutile en ignorant les 190 colonnes techniques non utilisées.</span></td></tr><tr><td><span>**RGPD**</span></td><td><span>On peut filtrer ou anonymiser les données sensibles avant qu'elles n'atteignent la base de données.</span></td></tr></tbody></table>

</div><div class="table-footer hide-from-message-actions"></div></div></div></div>---

### 🛠️ Vérification de l'import

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

<div class="code-block ng-tns-c2968650486-5621 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-bash-1"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2968650486-5621 ng-star-inserted"><span class="ng-tns-c2968650486-5621">Bash</span><div class="buttons ng-tns-c2968650486-5621 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2968650486-5621"><div class="animated-opacity ng-tns-c2968650486-5621"></div></div></div>```
docker exec -i alteris_postgis psql -U alteris_admin -d alteris_geo -c "SELECT count(*) FROM sitadel_2026_04.logements_chantiers;"

```

<div class="code-block ng-tns-c2968650486-5621 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--11"><div class="formatted-code-block-internal-container ng-tns-c2968650486-5621"><div class="animated-opacity ng-tns-c2968650486-5621"></div></div></div>**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.

<div class="code-block ng-tns-c2968650486-7679 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2968650486-7679 ng-star-inserted"><span class="ng-tns-c2968650486-7679">SQL</span><div class="buttons ng-tns-c2968650486-7679 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2968650486-7679"><div class="animated-opacity ng-tns-c2968650486-7679"></div></div></div>```
-- 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;

```

<div class="code-block ng-tns-c2968650486-7679 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--15"><div class="formatted-code-block-internal-container ng-tns-c2968650486-7679"><div class="animated-opacity ng-tns-c2968650486-7679"></div></div></div>---

### 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.

<div class="code-block ng-tns-c2968650486-7680 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-sql-1"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2968650486-7680 ng-star-inserted"><span class="ng-tns-c2968650486-7680">SQL</span><div class="buttons ng-tns-c2968650486-7680 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2968650486-7680"><div class="animated-opacity ng-tns-c2968650486-7680"></div></div></div>```
-- 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;

```

<div class="code-block ng-tns-c2968650486-7680 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--19"><div class="formatted-code-block-internal-container ng-tns-c2968650486-7680"><div class="animated-opacity ng-tns-c2968650486-7680"></div></div></div>---

### 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.

<div class="code-block ng-tns-c2968650486-7681 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk-bash-2"><div class="code-block-decoration header-formatted gds-title-s ng-tns-c2968650486-7681 ng-star-inserted"><span class="ng-tns-c2968650486-7681">Bash</span><div class="buttons ng-tns-c2968650486-7681 ng-star-inserted"></div></div><div class="formatted-code-block-internal-container ng-tns-c2968650486-7681"><div class="animated-opacity ng-tns-c2968650486-7681"></div></div></div>```
# 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

```

<div class="code-block ng-tns-c2968650486-7681 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" id="bkmrk--24"><div class="formatted-code-block-internal-container ng-tns-c2968650486-7681"><div class="animated-opacity ng-tns-c2968650486-7681"></div></div></div>---

**C'est la fin du volet "Données &amp; 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 ?