Les données géolocalisées se sont multipliées depuis quelques années, qu’il s’agisse de données open-data ou de traces numériques géolocalisées de type big-data. Pour les données spatiales, le package GeoPandas étend les fonctionalités de l’écosystème Pandas afin de permettre de manipuler des données géographiques complexes de manière simple.

Author

Lino Galiana

Published

July 9, 2023

Download nbviewer Onyxia Onyxia
Binder Open In Colab githubdev

Dans ce tutoriel, nous allons utiliser les données suivantes :

La représentation des données, notamment la cartographie, est présentée plus amplement dans la partie visualiser. Quelques méthodes pour faire rapidement des cartes seront présentées ici, mais l’objet de ce chapitre porte davantage sur la manipulation des données géographiques.

Ce tutoriel s’inspire beaucoup d’un autre tutoriel que j’ai fait pour R disponible dans la documentation utilitr. Il peut servir de pendant à celui-ci pour l’utilisateur de R.

Quelques installations préalables sont nécessaires :

!pip install pandas fiona shapely pyproj rtree # à faire obligatoirement en premier pour utiliser rtree ou pygeos pour les jointures spatiales
!pip install contextily
!pip install geopandas
!pip install topojson

Pour être en mesure d’exécuter ce tutoriel, les imports suivants seront utiles.

import geopandas as gpd
import contextily as ctx
import matplotlib.pyplot as plt

Données spatiales

Quelle différence avec des données traditionnelles ?

Le terme “données spatiales” désigne les données qui portent sur les caractéristiques géographiques des objets (localisation, contours, liens). Les caractéristiques géographiques des objets sont décrites à l’aide d’un système de coordonnées qui permettent une représentation dans un espace euclidien \((x,y)\). Le passage de l’espace réel (la Terre, qui est une sphère) à l’espace plan se fait grâce à un système de projection. Voici quelques exemples de données spatiales :

  • Une table décrivant des bâtiments, avec les coordonnées géographiques de chaque bâtiment ;
  • Le découpage communal du territoire, avec le contour du territoire de chaque commune ;
  • Les routes terrestres, avec les coordonnées décrivant leur parcours en 3 dimensions (longitude, latitude, altitude).

Les données spatiales rassemblent classiquement deux types de données :

  1. des données géographiques (ou géométries) : objets géométriques tels que des points, des vecteurs, des polygones, ou des maillages (raster). Exemple: la forme de chaque commune, les coordonnées d’un bâtiment;
  2. des données attributaires (ou attributs) : des mesures et des caractéristiques associées aux objets géométriques. Exemple: la population de chaque commune, le nombre de fenêtres et le nombre d’étages d’un bâtiment.

Les données spatiales sont fréquemment traitées à l’aide d’un système d’information géographique (SIG), c’est-à-dire un système d’information capable de stocker, d’organiser et de présenter des données alphanumériques spatialement référencées par des coordonnées dans un système de référence (CRS). Python dispose de fonctionnalités lui permettant de réaliser les mêmes tâches qu’un SIG (traitement de données spatiales, représentations cartographiques).

De Pandas à Geopandas

Le package Geopandas est une boîte à outils conçue pour faciliter la manipulation de données spatiales. La grande force de Geopandas est qu’il permet de manipuler des données spatiales comme s’il s’agissait de données traditionnelles, car il repose sur le standard ISO 19125 simple feature access défini conjointement par l’Open Geospatial Consortium (OGC) et l’International Organization for Standardization (ISO).

Par rapport à un DataFrame standard, un objet Geopandas comporte une colonne supplémentaire: geometry. Elle stocke les coordonnées des objets géographiques (ou ensemble de coordonnées s’agissant de contours). Un objet Geopandas hérite des propriétés d’un DataFrame Pandas mais propose des méthodes adaptées au traitement des données spatiales.

Ainsi, grâce à Geopandas, on pourra effectuer des manipulations sur les attributs des données comme avec pandas mais on pourra également faire des manipulations sur la dimension spatiale des données. En particulier,

  • Calculer des distances et des surfaces ;
  • Agréger rapidement des zonages (regrouper les communes en département par exemple) ;
  • Trouver dans quelle commune se trouve un bâtiment à partir de ses coordonnées géographiques ;
  • Recalculer des coordonnées dans un autre système de projection ;
  • Faire une carte, rapidement et simplement.

Par rapport à un logiciel spécialisé comme QGIS, Python permettra d’automatiser le traitement et la représentation des données. D’ailleurs, QGIS utilise lui-même Python

Résumé

En résumé, un objet GeoPandas comporte les éléments suivantes :

  1. Les attributs. Ce sont les valeurs associées à chaque niveau géographique. Il s’agit de la dimension tabulaire usuelle, dont le traitement est similaire à celui d’un objet Pandas classique.
  2. Les géométries. Ce sont les valeurs numériques interprétées pour représenter la dimension géographique. Elles permettent de représenter dans un certain référentiel (le système de référence) la dimension géographique.
  3. Le système de référence. Il s’agit du système permettant de transformer les positions sur le globe (3 dimensions avec une boule asymétrique) en un plan en deux dimensions. Il en existe une multitude, identifiables à partir d’un code EPSG (4326, 2154…). Leur manipulation est facilitée par Geopandas qui s’appuie sur Shapely, de la même manière que Pandas s’appuie sur Numpy ou Arrow.

Le système de projection cartographique

Principe

Les données spatiales sont plus riches que les données traditionnelles car elles incluent, habituellement, des éléments supplémentaires pour placer dans un espace cartésien les objets. Cette dimension supplémentaire peut être simple (un point comporte deux informations supplémentaire: \(x\) et \(y\)) ou assez complexe (polygones, lignes avec direction, etc.).

L’analyse cartographique emprunte dès lors à la géométrie des concepts pour représenter des objets dans l’espace. Les projections sont au coeur de la gestion des données spatiales. Ces dernières consistent à transformer une position dans l’espace terrestre à une position sur un plan. Il s’agit donc d’une opération de projection d’un espace tri-dimensionnel dans un espace à deux dimensions. Ce post propose de riches éléments sur le sujet, notamment l’image suivante qui montre bien le principe d’une projection :

Les différents types de projection

Cette opération n’est pas neutre. L’une des conséquences du théorème remarquable de Gauss est que la surface de la Terre ne peut être cartographiée sans distortion. Une projection ne peut simultanément conserver intactes les distances et les angles (i.e. les positions). Il n’existe ainsi pas de projection universellement meilleure, ce qui ouvre la porte à la coexistence de nombreuses projections différentes, pensées pour des tâches différentes. Un mauvais système de représentation fausse l’appréciation visuelle mais peut aussi entraîner des erreurs dans les calculs sur la dimension spatiale.

Les systèmes de projection font l’objet de standards internationaux et sont souvent désignés par des codes dits codes EPSG. Ce site est un bon aide-mémoire. Les plus fréquents, pour les utilisateurs français, sont les suivants (plus d’infos ici) :

  • 2154 : système de projection Lambert 93. Il s’agit du système de projection officiel. La plupart des données diffusées par l’administration pour la métropole sont disponibles dans ce système de projection.
  • 27572 : Lambert II étendu. Il s’agit de l’ancien système de projection officiel. Les données spatiales anciennes peuvent être dans ce format.
  • 4326 : WGS 84 ou système de pseudo-Mercator ou encore Web Mercator. Ce n’est en réalité pas un système de projection mais un système de coordonnées (longitude / latitude) qui permet simplement un repérage angulaire sur l’ellipsoïde. Il est utilisé pour les données GPS. Il s’agit du système le plus usuel, notamment quand on travaille avec des fonds de carte web.

Comme évoqué plus haut, l’une des projections les plus connues est la projection Web Mercator dite WGS84 (code EPSG 4326). Il s’agit d’une projection conservant intacte les angles, ce qui implique qu’elle altère les distances. Celle-ci a en effet été pensée, à l’origine, pour représenter l’hémisphère Nord. Plus on s’éloigne de celui-ci, plus les distances sont distordues. Cela amène à des distorsions bien connues (le Groenland hypertrophié, l’Afrique de taille réduite, l’Antarctique démesuré…). En revanche, la projection Mercator conserve intacte les positions. C’est cette propriété qui explique son utilisation dans les systèmes GPS et ainsi dans les fonds de carte de navigation du type Google Maps.

Exemple de reprojection de pays depuis le site thetruesize.com

Observez les variations significatives de proportions pour certains pays selon les projections choisies:

Pour aller plus loin, la carte interactive suivante, construite par Nicolas Lambert, issue de ce notebook Observable, illustre l’effet déformant de la projection Mercator, et de quelques-unes autres, sur notre perception de la taille des pays.

Voir la carte interactive

Il existe en fait de nombreuses représentations possibles du monde, plus ou moins alambiquées. Les projections sont très nombreuses et certaines peuvent avoir une forme suprenante. Par exemple, la projection de Spillhaus propose de centrer la vue sur les océans et non une terre. C’est pour cette raison qu’on parle parfois de monde tel que vu par les poissons à son propos.

Importer des données spatiales

Les formats les plus communs de données spatiales sont les suivants :

  • shapefile (.shp) : format (propriétaire) le plus commun de données géographiques. La table de données (attributs) est stockée dans un fichier séparé des données spatiales. En faisant geopandas.read_file("monfichier.shp"), le package fait lui-même le lien entre les observations et leur représentation spatiale.
  • geopackage (.gpkg) : ce (relativement) nouveau format libre en un seul fichier également (lui recommandé par l’OGC) vise progressivement à se substituer au shapefile. Il est par exemple le format par défaut dans QGIS.
  • geojson (.json) : ce format, non préconisé par l’OGC, est largement utilisé pour le développement web comme dans la librairie leaflet.js. La dimension spatiale est stockée dans le même fichier que les attributs. Ces fichiers sont généralement beaucoup plus légers que les shapefiles mais possèdent des limites s’agissant de gros jeux de données.
  • topojson (.json) : une variante du geojson qui se développe progressivement pour assister les visualisations web. Au lieu de stocker l’ensemble des points permettant de représenter une géométrie, seuls les arcs sont conservés. Cela allège substantiellement le poids du fichier et permet, avec une librairie adaptée, de reconstruire l’ensemble des contours géographiques.

Cette page compare plus en détail les principes formats de données géographiques. L’aide de Geopandas propose des bouts de code en fonction des différentes situations dans lesquelles on se trouve.

Exemple : récupérer les découpages territoriaux

L’un des fonds de carte les plus fréquents qu’on utilise est celui des limites administratives des communes. Celui-ci peut être récupéré de plusieurs manières. En premier lieu, pour récupérer le fond de carte officiel, produit par l’IGN, sous le nom d’AdminExpress[^1], il est possible de se rendre sur le site de l’IGN et de le télécharger. Il est également possible d’utiliser l’une des API de l’IGN mais ces dernières ne sont pas encore très documentées pour des utilisateurs de Python. Le package cartiflette, issu d’un projet interministériel, propose une récupération facilitée de fonds de carte officiels de l’IGN. Ce projet vise à faciliter la récupération des sources officielles, notamment celles de l’IGN, et leur association à des jeux de données géographiques.

Ici, nous sommes intéressés par les contours des communes de la petite couronne. On pourrait désirer récupérer l’ensemble de la région Ile-de-France mais nous allons nous contenter de l’analyse de Paris intra-muros et des départements limitrophes. C’est l’un des avantage de cartiflette que de faciliter la récupération de fonds de carte sur un ensemble de département. Cela évite la récupération d’un fond de carte très volumineux (plus de 500Mo) pour une analyse restreinte (quelques départements). Un autre avantage de cartiflette est de faciliter la récupération de fonds de carte consolidés comme celui dont on a besoin ici : arrondissements dans Paris, communes ailleurs. Comme cela est expliqué dans un encadré à part, il s’agirait d’une opération pénible à mettre en oeuvre sans cartiflette.

Les contours de cet espace peuvent être récupérés de la manière suivante :

import cartiflette.s3 as s3

shp_communes = s3.download_vectorfile_url_all(
    crs = 4326,
    values = ["75", "92", "93", "94"],
    borders="COMMUNE_ARRONDISSEMENT",
    vectorfile_format="topojson",
    filter_by="DEPARTEMENT",
    source="EXPRESS-COG-CARTO-TERRITOIRE",
    year=2022)
shp_communes.head(3)
id ID NOM NOM_M INSEE_COM STATUT POPULATION INSEE_CAN INSEE_ARR INSEE_DEP INSEE_REG SIREN_EPCI source INSEE_COG geometry
0 ARR_MUNI0000000009736045 NaN Paris 3e Arrondissement PARIS 3E ARRONDISSEMENT 75056 Capitale d'état 34025 NR 1 75 11 200054781 IGN:EXPRESS-COG-CARTO-TERRITOIRE 75103 POLYGON ((2.35016 48.86199, 2.35019 48.86203, ...
1 ARR_MUNI0000000009736046 NaN Paris 2e Arrondissement PARIS 2E ARRONDISSEMENT 75056 Capitale d'état 21595 NR 1 75 11 200054781 IGN:EXPRESS-COG-CARTO-TERRITOIRE 75102 POLYGON ((2.34792 48.87069, 2.34827 48.87062, ...
2 ARR_MUNI0000000009736545 NaN Paris 4e Arrondissement PARIS 4E ARRONDISSEMENT 75056 Capitale d'état 29131 NR 1 75 11 200054781 IGN:EXPRESS-COG-CARTO-TERRITOIRE 75104 POLYGON ((2.36849 48.85580, 2.36873 48.85482, ...

On reconnaît la structure d’un DataFrame Pandas. A cette structure s’ajoute une colonne geometry qui enregistre la position des limites des polygones de chaque observation.

Comme vu précédemment, le système de projection est un élément important. Il permet à Python d’interpréter les valeurs des points (deux dimensions) en position sur la terre, qui n’est pas un espace plan.

shp_communes.crs
<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World.
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich

Ici, les données sont dans le système WGS84 (code EPSG 4326). Ce n’est pas le Lambert-93 comme on pourrait s’y attendre, ce dernier étant le système légal de projection pour la France métropolitaine.

Pour s’assurer qu’on a bien récupéré les contours voulus, on peut représenter graphiquement les contours grâce à la méthode plot sur laquelle nous reviendrons :

ax = shp_communes.boundary.plot()
ax.set_axis_off()

Opérations sur les attributs et les géométries

Import des données vélib

Souvent, le découpage communal ne sert qu’en fond de cartes, pour donner des repères. En complément de celui-ci, on peut désirer exploiter un autre jeu de données. On va partir des données de localisation des stations velib, disponibles sur le site d’open data de la ville de Paris et requêtables directement par l’url https://opendata.paris.fr/explore/dataset/velib-emplacement-des-stations/download/?format=geojson&timezone=Europe/Berlin&lang=fr

velib_data = 'https://opendata.paris.fr/explore/dataset/velib-emplacement-des-stations/download/?format=geojson&timezone=Europe/Berlin&lang=fr'
stations = gpd.read_file(velib_data)
stations.crs
<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World.
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich

Les données sont dans le système de projection WGS84 qui est celui du système GPS. Celui-ci s’intègre bien avec les fonds de carte OpenStreetMap ou Google Maps. En toute rigueur, si on désire effectuer certains calculs géométriques (mesurer des surfaces…), il est nécessaire de re-projeter les données dans un système qui préserve la géométrie (c’est le cas du Lambert 93).

Pour avoir une intuition de la localisation des stations, et notamment de la densité hétérogène de celles-ci, on peut afficher les données sur la carte des communes de la petite couronne. Il s’agit donc d’enrichir la carte précédente d’une couche supplémentaire, à savoir la localisation des stations. Au passage, on va utiliser un fond de carte plus esthétique:

fig,ax = plt.subplots(figsize=(10, 10))
stations.sample(200).to_crs(3857).plot(ax = ax, color = 'red', alpha = 0.4, zorder=2)
shp_communes.to_crs(3857).plot(ax = ax, zorder=1, edgecolor = "black", facecolor="none",
                                                           color = None)
ctx.add_basemap(ax, source = ctx.providers.OpenStreetMap.Mapnik)
ax.set_axis_off()

Découvrez ci-dessous par étape les différentes lignes de commandes permettant d’afficher cette carte complète, étape par étape :

1️⃣ Afficher le nuage de points de 200 stations vélibs prises au hasard

fig, ax = plt.subplots(figsize=(10, 10))
stations.sample(200).to_crs(3857).plot(ax = ax, color = 'red', alpha = 0.4, zorder=2)
<Axes: >

2️⃣ Ajouter à cette couche, en-dessous, les contours des communes

fig, ax = plt.subplots(figsize=(10, 10))
stations.sample(200).to_crs(3857).plot(ax = ax, color = 'red', alpha = 0.4, zorder=2)
shp_communes.to_crs(3857).plot(ax = ax, zorder=1, edgecolor = "black", facecolor="none",
                                                           color = None)

3️⃣ Ajouter un fond de carte de type open street map grâce au package contextily

fig,ax = plt.subplots(figsize=(10, 10))
stations.sample(200).to_crs(3857).plot(ax = ax, color = 'red', alpha = 0.4, zorder=2)
shp_communes.to_crs(3857).plot(ax = ax, zorder=1, edgecolor = "black", facecolor="none",
                                                           color = None)
ctx.add_basemap(ax, source = ctx.providers.OpenStreetMap.Mapnik)

4️⃣ Il ne reste plus qu’à retirer l’axe des coordonnées, qui n’est pas très esthétique.

fig,ax = plt.subplots(figsize=(10, 10))
stations.sample(200).to_crs(3857).plot(ax = ax, color = 'red', alpha = 0.4, zorder=2)
shp_communes.to_crs(3857).plot(ax = ax, zorder=1, edgecolor = "black", facecolor="none",
                                                           color = None)
ctx.add_basemap(ax, source = ctx.providers.OpenStreetMap.Mapnik)
ax.set_axis_off()
ax

In fine, on obtient la carte désirée.

Opérations sur les attributs

Toutes les opérations possibles sur un objet Pandas le sont également sur un objet GeoPandas. Pour manipuler les données, et non la géométrie, on parlera d’opérations sur les attributs.

Par exemple, si on désire connaître quelques statistiques sur la taille des stations, l’approche est identique à si on avait un objet Pandas classique :

stations.describe()
capacity
count 1468.000000
mean 31.059946
std 12.008538
min 0.000000
25% 23.000000
50% 29.000000
75% 37.000000
max 74.000000

Pour classer les départements de la petite couronne, du plus grand au plus petit, procédons en deux étapes:

  1. Récupérons le contour des communes grâce à cartiflette. Notons qu’on pourrait récupérer directement les contours départementaux mais pour l’exercice, nous allons le créer nous-mêmes comme agrégation des contours communaux (voir plus bas ainsi que ce notebook Observable pour la méthode plus légère qui utilise pleinement les fonctionnalités de cartiflette).

  2. Calculons la surface totale de ce territoire (méthode area sur un objet GeoPandas.GeoDataFrame ramenée en km², attention néamoins au système de projection comme cela est expliqué plus bas)

shp_communes['surface'] = shp_communes.area.div(10**6)

Les plus grands départements s’obtiennent par une agrégation des surfaces communales :

shp_communes.groupby('INSEE_DEP').sum(numeric_only = True).sort_values('surface', ascending = False)
ID POPULATION surface
INSEE_DEP
94 0.0 1407124 244.811517
93 0.0 1644903 236.868125
92 0.0 1624357 175.570765
75 0.0 2165423 105.431335

Si on veut directement les plus grandes communes de la petite couronne parisienne :

shp_communes.sort_values('surface', ascending = False).head(10)
id ID NOM NOM_M INSEE_COM STATUT POPULATION INSEE_CAN INSEE_ARR INSEE_DEP INSEE_REG SIREN_EPCI source geometry INSEE_COG surface
23 COMMUNE_0000000009735015 NaN Tremblay-en-France TREMBLAY-EN-FRANCE 93073 Commune simple 36461 20 2 93 11 200054781/200058097 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((663432.643 6875170.586, 663414.781 6... NaN 22.680693
12 ARR_MUNI0000000009736553 NaN Paris 16e Arrondissement PARIS 16E ARRONDISSEMENT 75056 Capitale d'état 165523 NR 1 75 11 200054781 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((647190.220 6864524.360, 647201.518 6... 75116 16.409713
19 ARR_MUNI0000000009736532 NaN Paris 12e Arrondissement PARIS 12E ARRONDISSEMENT 75056 Capitale d'état 139297 NR 1 75 11 200054781 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((655221.113 6858576.460, 655149.729 6... 75112 16.380842
7 COMMUNE_0000000009735500 NaN Aulnay-sous-Bois AULNAY-SOUS-BOIS 93005 Commune simple 86969 02 2 93 11 200054781/200058097 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((660415.819 6872923.194, 660423.603 6... NaN 16.167599
31 COMMUNE_0000000009736056 NaN Rueil-Malmaison RUEIL-MALMAISON 92063 Commune simple 78317 22 2 92 11 200054781/200057982 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((639099.023 6866521.194, 639135.281 6... NaN 14.540955
37 COMMUNE_0000000009736517 NaN Noisy-le-Grand NOISY-LE-GRAND 93051 Commune simple 67871 14 2 93 11 200054781/200058790 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((664453.846 6861358.828, 664461.038 6... NaN 13.138755
0 COMMUNE_0000000009735515 NaN Saint-Denis SAINT-DENIS 93066 Sous-préfecture 112852 99 3 93 11 200054781/200057867 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((652098.246 6872022.511, 652102.652 6... NaN 12.372213
4 COMMUNE_0000000009736052 NaN Nanterre NANTERRE 92050 Préfecture 96277 99 2 92 11 200054781/200057982 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((643490.675 6867612.283, 643581.446 6... NaN 12.231274
37 COMMUNE_0000000009737009 NaN Vitry-sur-Seine VITRY-SUR-SEINE 94081 Commune simple 95510 99 3 94 11 200054781/200058014 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((653536.980 6852608.424, 653536.652 6... NaN 11.665507
20 COMMUNE_0000000009735517 NaN Gennevilliers GENNEVILLIERS 92036 Commune simple 48530 14 2 92 11 200054781/200057990 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((649583.723 6872218.087, 649556.138 6... NaN 11.632021

Lors des étapes d’agrégation, groupby ne conserve pas les géométries. Autrement dit, si on effectue, par exemple, une somme en fonction d’une variable de groupe avec le combo groupby(...).sum(...) , on perd la dimension géographique.

Il est néanmoins possible d’aggréger à la fois les géométries et les attribus avec la méthode dissolve:

fig,ax = plt.subplots(figsize=(10, 10))
shp_communes.dissolve(by='INSEE_DEP', aggfunc='sum').plot(ax = ax, column = "surface")
ax.set_axis_off()
ax
/opt/mamba/lib/python3.9/site-packages/geopandas/plotting.py:730: FutureWarning:

is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead
<Axes: >

Pour produire l’équivalent de cette carte à un niveau France entière, il est néanmoins plus simple de directement récupérer les fonds officiels des départements plutôt que d’agréger les contours des communes:

dep = s3.download_vectorfile_url_all(
    values = "metropole",
    crs = 4326,
    borders = "DEPARTEMENT",
    vectorfile_format="topojson",
    filter_by="FRANCE_ENTIERE",
    source="EXPRESS-COG-CARTO-TERRITOIRE",
    year=2022)

dep["area"] = dep.to_crs(2154).area

Avant de calculer les surfaces des départements, pour éviter les déformations liées au système Mercator, nous faisons une reprojection des données à la volée. Plus de détails par la suite.

dep.sort_values('area', ascending = False).head(3)
id ID NOM_M NOM INSEE_DEP INSEE_REG source territoire geometry area
33 DEPARTEM_FXX_00000000034 NaN GIRONDE Gironde 33 75 IGN:EXPRESS-COG-CARTO-TERRITOIRE metropole MULTIPOLYGON (((-1.15275 45.60453, -1.15084 45... 1.036783e+10
40 DEPARTEM_FXX_00000000041 NaN LANDES Landes 40 75 IGN:EXPRESS-COG-CARTO-TERRITOIRE metropole POLYGON ((-1.25361 44.46752, -1.25317 44.46762... 9.354177e+09
24 DEPARTEM_FXX_00000000025 NaN DORDOGNE Dordogne 24 75 IGN:EXPRESS-COG-CARTO-TERRITOIRE metropole POLYGON ((-0.04044 45.10261, -0.04073 45.10309... 9.210826e+09
ax = dep.plot(column = "area")
ax.set_axis_off()
/opt/mamba/lib/python3.9/site-packages/geopandas/plotting.py:730: FutureWarning:

is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead

Opérations sur les géométries

Outre la représentation graphique simplifiée, sur laquelle nous reviendrons ultérieurement, l’intérêt principal d’utiliser GeoPandas est l’existence de méthodes efficaces pour manipuler la dimension spatiale. Un certain nombre proviennent du package Shapely.

Comme indiqué ci-dessus, nous reprojetons les données dans le système Lambert 93 qui ne fausse pas les calculs de distance et d’aires.

communes = shp_communes.to_crs(2154)
stations = stations.to_crs(2154)

Par exemple, on peut recalculer la taille d’une commune ou d’arrondissement avec la méthode area (et diviser par \(10^6\) pour avoir des \(km^2\) au lieu des \(m^2\)):

communes['superficie'] = communes.area.div(10**6)
communes.head(3)
id ID NOM NOM_M INSEE_COM STATUT POPULATION INSEE_CAN INSEE_ARR INSEE_DEP INSEE_REG SIREN_EPCI source geometry INSEE_COG surface superficie
0 COMMUNE_0000000009736037 NaN Levallois-Perret LEVALLOIS-PERRET 92044 Commune simple 66082 16 2 92 11 200054781/200057982 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((647761.341 6867306.988, 647839.223 6... NaN 2.417491 2.417491
1 COMMUNE_0000000009736055 NaN Bois-Colombes BOIS-COLOMBES 92009 Commune simple 28841 11 2 92 11 200054781/200057990 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((646224.655 6867615.810, 646228.990 6... NaN 1.926619 1.926619
2 COMMUNE_0000000009736538 NaN Malakoff MALAKOFF 92046 Commune simple 30950 18 1 92 11 200054781/200057966 IGN:EXPRESS-COG-CARTO-TERRITOIRE POLYGON ((646995.323 6857373.499, 647177.485 6... NaN 2.070953 2.070953

Une méthode qu’on utilise régulièrement est centroid qui, comme son nom l’indique, recherche le centroïde de chaque polygone et transforme ainsi des données surfaciques en données ponctuelles. Par exemple, pour représenter approximativement les centres des villages de la Haute-Garonne (31), après avoir téléchargé le fonds de carte adapté, fera

communes_31 = s3.download_vectorfile_url_all(
      crs = 4326,
      values = "31",
      borders="COMMUNE",
      vectorfile_format="topojson",
      filter_by="DEPARTEMENT",
      source="EXPRESS-COG-CARTO-TERRITOIRE",
      year=2022)

# on reprojete en 3857 pour le fond de carte
communes_31 = communes_31.to_crs(3857)

# on calcule le centroide
dep_31 = communes_31.copy()
communes_31['geometry'] = communes_31['geometry'].centroid

ax = communes_31.plot(figsize = (10,10), color = 'red', alpha = 0.4, zorder=2)
dep_31.to_crs(3857).plot(ax = ax, zorder=1, edgecolor = "black", facecolor="none",
                                                           color = None)
#ctx.add_basemap(ax, source = ctx.providers.Stamen.Toner)
ax.set_axis_off()
ax
https://minio.lab.sspcloud.fr/projet-cartiflette/diffusion/shapefiles-test1/year=2022/administrative_level=COMMUNE/crs=4326/DEPARTEMENT=31/vectorfile_format=topojson/provider=IGN/source=EXPRESS-COG-CARTO-TERRITOIRE/raw.topojson
Downloading: : 0.00iB [00:00, ?iB/s]Downloading: : 8.00kiB [00:00, 64.3kiB/s]Downloading: : 34.0kiB [00:00, 131kiB/s] Downloading: : 82.0kiB [00:00, 218kiB/s]Downloading: : 166kiB [00:00, 351kiB/s] Downloading: : 338kiB [00:00, 634kiB/s]Downloading: : 682kiB [00:00, 1.19MiB/s]Downloading: : 1.34MiB [00:01, 2.27MiB/s]Downloading: : 1.55MiB [00:01, 1.79MiB/s]Downloading: : 1.60MiB [00:01, 1.30MiB/s]
<Axes: >

Gérer le système de projection

Précédemment, nous avons appliqué une méthode to_crs pour reprojeter les données dans un système de projection différent de celui du fichier d’origine :

communes = communes.to_crs(2154)
stations = stations.to_crs(2154)

Concernant la gestion des projections avec GeoPandas, la documentation officielle est très bien faite. Elle fournit notamment l’avertissement suivant qu’il est bon d’avoir en tête :

Be aware that most of the time you don’t have to set a projection. Data loaded from a reputable source (using the geopandas.read_file() command) should always include projection information. You can see an objects current CRS through the GeoSeries.crs attribute.

From time to time, however, you may get data that does not include a projection. In this situation, you have to set the CRS so geopandas knows how to interpret the coordinates.

Image empruntée à XKCD https://xkcd.com/2256/ qu’on peut également trouver sur https://blog.chrislansdown.com/2020/01/17/a-great-map-projection-joke/

Pour déterminer le système de projection d’une base de données, on peut vérifier l’attribut crs :

communes.crs
<Projected CRS: EPSG:2154>
Name: RGF93 v1 / Lambert-93
Axis Info [cartesian]:
- X[east]: Easting (metre)
- Y[north]: Northing (metre)
Area of Use:
- name: France - onshore and offshore, mainland and Corsica (France métropolitaine including Corsica).
- bounds: (-9.86, 41.15, 10.38, 51.56)
Coordinate Operation:
- name: Lambert-93
- method: Lambert Conic Conformal (2SP)
Datum: Reseau Geodesique Francais 1993 v1
- Ellipsoid: GRS 1980
- Prime Meridian: Greenwich

Les deux principales méthodes pour définir le système de projection utilisé sont :

  • df.set_crs : cette commande sert à préciser quel est le système de projection utilisé, c’est-à-dire comment les coordonnées (x,y) sont reliées à la surface terrestre. Cette commande ne doit pas être utilisée pour transformer le système de coordonnées, seulement pour le définir.
  • df.to_crs : cette commande sert à projeter les points d’une géométrie dans une autre, c’est-à-dire à recalculer les coordonnées selon un autre système de projection.

Dans le cas particulier de production de carte avec un fond OpenStreetMaps ou une carte dynamique leaflet, il est nécessaire de dé-projeter les données (par exemple à partir du Lambert-93) pour atterrir dans le système non-projeté WGS 84 (code EPSG 4326). Ce site dédié aux projections géographiques peut être utile pour retrouver le système de projection d’un fichier où il n’est pas indiqué.

La définition du système de projection se fait de la manière suivante (:warning: avant de le faire, se souvenir de l’avertissement !) :

communes = communes.set_crs(2154)

Alors que la reprojection (projection Albers : 5070) s’obtient de la manière suivante :

shp_region = s3.download_vectorfile_url_all(
    values = "metropole",
    crs = 4326,
    borders = "REGION",
    vectorfile_format="topojson",
    filter_by="FRANCE_ENTIERE",
    source="EXPRESS-COG-CARTO-TERRITOIRE",
    year=2022)
    
fig,ax = plt.subplots(figsize=(10, 10))
shp_region.to_crs(5070).plot(ax = ax)
ax
https://minio.lab.sspcloud.fr/projet-cartiflette/diffusion/shapefiles-test1/year=2022/administrative_level=REGION/crs=4326/FRANCE_ENTIERE=metropole/vectorfile_format=topojson/provider=IGN/source=EXPRESS-COG-CARTO-TERRITOIRE/raw.topojson
Downloading: : 0.00iB [00:00, ?iB/s]Downloading: : 12.0kiB [00:00, 79.2kiB/s]Downloading: : 40.0kiB [00:00, 141kiB/s] Downloading: : 88.0kiB [00:00, 220kiB/s]Downloading: : 176kiB [00:00, 360kiB/s] Downloading: : 356kiB [00:00, 655kiB/s]Downloading: : 712kiB [00:00, 1.22MiB/s]Downloading: : 1.40MiB [00:01, 2.36MiB/s]Downloading: : 2.64MiB [00:01, 4.82MiB/s]Downloading: : 4.11MiB [00:01, 7.42MiB/s]Downloading: : 4.76MiB [00:01, 3.71MiB/s]
<Axes: >

<Figure size 672x480 with 0 Axes>

On le voit, cela modifie totalement la représentation de l’objet dans l’espace. Clairement, cette projection n’est pas adaptée aux longitudes et latitudes françaises. C’est normal, il s’agit d’une projection adaptée au continent nord-américain (et encore, pas dans son ensemble !).

world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))

fig,ax = plt.subplots(figsize=(10, 10))
world[world.continent == "North America"].to_crs(5070).plot(alpha = 0.2, edgecolor = "k", ax = ax)
ax
<Axes: >

<Figure size 672x480 with 0 Axes>

Joindre des données

Joindre des données sur des attributs

Ce type de jointure se fait entre un objet géographique et un deuxième objet, géographique ou non. A l’exception de la question des géométries, il n’y a pas de différence par rapport à Pandas.

La seule différence avec Pandas est dans la dimension géographique. Si on désire conserver la dimension géographique, il faut faire attention à faire :

geopandas_object.merge(pandas_object)

Si on utilise deux objets géographiques mais ne désire conserver qu’une seule dimension géographique1, on fera

geopandas_object1.merge(geopandas_object2)

Seule la géométrie de l’objet de gauche sera conservée, même si on fait un right join.

Prolongation possible : joindre des données sur dimension géographique

Le chapitre suivant permettra de mettre en oeuvre des jointures géographiques.

Back to top

Footnotes

  1. Il est techniquement possible d’avoir un DataFrame comportant plusieurs géographies. Par exemple une géométrie polygone et une géométrie point (le centroid). C’est néanmoins souvent compliqué à gérer et donc peu recommandable.↩︎

Citation

BibTeX citation:
@book{galiana2023,
  author = {Galiana, Lino},
  title = {Python Pour La Data Science},
  date = {2023},
  url = {https://pythonds.linogaliana.fr/},
  doi = {10.5281/zenodo.8229676},
  langid = {en}
}
For attribution, please cite this work as:
Galiana, Lino. 2023. Python Pour La Data Science. https://doi.org/10.5281/zenodo.8229676.