Skip to main content

REX 06079

Contexte

Affichage des IRIS de Mandelieu-la-Napoule (06079) depuis PostGIS Alteris, avec population graduée et étiquettes — entièrement piloté depuis Claude Code via le MCP QGIS, sans interaction manuelle dans QGIS.


Données utilisées

Table Schéma Contenu contours_iris_2025 insee_raw Géométries IRIS France entière iris_2025_population insee_raw Population 2022 par IRIS (recensement)

Colonnes clés — pièges à retenir

contours_iris_2025 : colonnes standard sans espace — code_insee, code_iris, nom_iris, type_iris, geom

iris_2025_population : colonnes avec espace traînant (import INSEE brut) :

    Clé IRIS : "IRIS " (avec espace) Commune : "COM " (avec espace) Population totale 2022 : "P22_POP " (avec espace) Toutes les colonnes sont de type text — caster en ::numeric avant usage

    Résultats

    9 IRIS pour 06079, population 2022 :

    code_iris nom_iris population 060790101 Zone d'activités 24 060790102 IRIS 2 2 701 060790103 IRIS 3 2 000 060790104 IRIS 4 3 058 060790105 IRIS 5 2 197 060790106 IRIS 6 2 432 060790107 IRIS 7 3 188 060790108 IRIS 8 2 944 060790109 IRIS 9 2 656

    Population min : 24 hab. (zone d'activités/naturelle) — max : 3 188 hab.


    Procédure complète

    Prérequis

    Tunnel SSH paramiko actif (voir page 229 — Option 2) :

    import sys
    sys.path.insert(0, r'C:\Users\eliob\AppData\Roaming\Python\Python312\site-packages')
    import paramiko
    # ... lancer SSHTunnel vers localhost:5433
    

    1. Identifier les tables

    import psycopg2
    conn = psycopg2.connect(host='localhost', port=5433, dbname='alteris_geo',
                            user='alteris_admin', password='Alteris2026')
    cur = conn.cursor()
    cur.execute("SELECT table_schema, table_name FROM information_schema.tables WHERE table_name ILIKE '%iris%' ORDER BY 1,2")
    print(cur.fetchall())
    

    2. Créer une vue PostgreSQL (contournement du bug URI)

    Problème rencontré : charger une sous-requête SQL directement dans l'URI QGIS échoue quand la requête contient des guillemets doubles ("P22_POP "). QgsDataSourceUri.setDataSource() sur-échappe les guillemets (\\\") → couche invalide.

    Solution : créer une vue PostgreSQL et charger la vue comme une table normale.

    cur.execute("""
        CREATE OR REPLACE VIEW insee_raw.v_iris_mandelieu_pop AS
        SELECT c.fid, c.geom, c.nom_iris, c.code_iris,
               ROUND(p."P22_POP "::numeric, 0)::integer AS population
        FROM insee_raw.contours_iris_2025 c
        LEFT JOIN insee_raw.iris_2025_population p ON p."IRIS " = c.code_iris
        WHERE c.code_insee = '06079'
    """)
    conn.commit()
    conn.close()
    

    3. Charger la vue comme couche QGIS

    from qgis.core import QgsProject, QgsVectorLayer
    
    uri = (
        'host=localhost port=5433 dbname=alteris_geo '
        'user=alteris_admin password=Alteris2026 sslmode=disable '
        'table="insee_raw"."v_iris_mandelieu_pop" (geom) key=fid'
    )
    layer = QgsVectorLayer(uri, "IRIS Mandelieu-la-Napoule", "postgres")
    # → Valide: True — Entités: 9
    

    4. Graduation par population

    from qgis.core import QgsGraduatedSymbolRenderer, QgsRendererRange, QgsFillSymbol
    
    colors = ['#EFF3FF', '#BDD7E7', '#6BAED6', '#2171B5', '#084594']  # blanc → bleu foncé
    pmin, pmax = 24, 3188
    step = (pmax - pmin) / 5
    bounds = [pmin + i * step for i in range(6)]
    
    ranges_def = []
    for i in range(5):
        lo, hi = bounds[i], bounds[i+1]
        sym = QgsFillSymbol.createSimple({
            'color': colors[i], 'outline_color': '#555555', 'outline_width': '0.3'
        })
        label = f"{int(lo):,}–{int(hi):,} hab.".replace(',', ' ')
        ranges_def.append(QgsRendererRange(lo, hi, sym, label))
    
    renderer = QgsGraduatedSymbolRenderer('population', ranges_def)
    layer.setRenderer(renderer)
    

    5. Étiquettes (nom IRIS + population)

    from qgis.core import (
        QgsPalLayerSettings, QgsTextFormat, QgsTextBufferSettings,
        QgsVectorLayerSimpleLabeling
    )
    from qgis.PyQt.QtGui import QColor, QFont
    
    pal = QgsPalLayerSettings()
    pal.fieldName = "concat(nom_iris, '\n', population, ' hab.')"
    pal.isExpression = True
    pal.placement = QgsPalLayerSettings.Placement.OverPoint
    
    fmt = QgsTextFormat()
    fmt.setFont(QFont("Arial", 8))
    fmt.setSize(8)
    fmt.setColor(QColor('#1a1a1a'))
    
    buf = QgsTextBufferSettings()
    buf.setEnabled(True)
    buf.setSize(1)
    buf.setColor(QColor('white'))
    fmt.setBuffer(buf)
    pal.setFormat(fmt)
    
    layer.setLabelsEnabled(True)
    layer.setLabeling(QgsVectorLayerSimpleLabeling(pal))
    QgsProject.instance().addMapLayer(layer)
    

    Points clés et pièges

    Sujet Constat Colonnes INSEE avec espace Toutes les colonnes de iris_2025_population ont un espace traînant — utiliser "IRIS ", "COM ", "P22_POP " Types text Toutes les valeurs numériques sont stockées en text — caster avec ::numeric Sous-requête URI QGIS QgsDataSourceUri.setDataSource() sur-échappe les guillemets doubles → couche invalide. Contournement : créer une vue PostgreSQL sys.executable QGIS Pointe sur qgis-bin.exe, pas python.exe — subprocess/pip inutilisable directement Import manquant QgsVectorLayerSimpleLabeling doit être importé explicitement depuis qgis.core

    Généralisation à d'autres communes

    Remplacer '06079' par le code INSEE cible dans la vue :

    cur.execute(f"""
        CREATE OR REPLACE VIEW insee_raw.v_iris_pop AS
        SELECT c.fid, c.geom, c.nom_iris, c.code_iris, c.nom_commune,
               ROUND(p."P22_POP "::numeric, 0)::integer AS population
        FROM insee_raw.contours_iris_2025 c
        LEFT JOIN insee_raw.iris_2025_population p ON p."IRIS " = c.code_iris
        WHERE c.code_insee = '{code_insee}'
    """)