-
Notifications
You must be signed in to change notification settings - Fork 45
/
index.qmd
761 lines (592 loc) · 25.2 KB
/
index.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
---
title: "Pratique de geopandas avec les données vélib"
date: 2020-07-09T13:00:00Z
draft: false
weight: 50
slug: geopandasTP
tags:
- geopandas
- Velib
- Exercice
- Cartographie
- Manipulation
categories:
- Manipulation
- Exercice
type: book
summary: |
Ce chapitre illustre les fonctionalités de `GeoPandas` à partir des
décomptes de vélo fournis par la ville de Paris
en [opendata](https://opendata.paris.fr/explore/dataset/comptage-velo-donnees-compteurs/map/?disjunctive.id_compteur&disjunctive.nom_compteur&disjunctive.id&disjunctive.name&basemap=jawg.dark&location=12,48.85855,2.33754).
Il prolonge
le chapitre précédent avec des données un petit peu plus complexes
à manipuler.
echo: false
---
::: {.cell .markdown}
```{python}
#| echo: false
#| output: 'asis'
#| include: true
#| eval: true
import sys
sys.path.insert(1, '../../../../') #insert the utils module
from utils import print_badges
#print_badges(__file__)
print_badges("content/course/manipulation/03_geopandas_TP.qmd")
```
:::
Installations préalables :
```{python}
#| echo: true
#| eval: false
!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 pygeos
```
Les instructions d'installation du package `cartiflette`
sont quant à elles détaillées dans le chapitre
précédent.
```{python}
#| echo: true
#| output: false
import geopandas as gpd
```
# Lire et enrichir des données spatiales
Dans cette partie,
nous utiliserons
le package [`cartiflette`](https://github.com/InseeFrLab/cartiflette.git)
qui facilite la récupération de contours de cartes.
Une version antérieure de cet exercice, présentée sous forme
d'[exercice supplémentaire 👇️](#exo-supp), utilisait des fonds de carte issus
de `data.gouv`.
```{python}
# 0) Chargement des packages utilisés dans la partie tutoriel
import geopandas as gpd
import contextily as ctx
import matplotlib.pyplot as plt
import cartiflette.s3
```
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice 1: lire et explorer la structure de fichiers géographiques</h3>
```
1. S'inspirer des exemples de code présents dans le chapitre précédent, mobilisant
le package `cartiflette`
pour télécharger les données communales des départements 75, 92, 93 et 94.
Vous pouvez nommer l'objet `communes_borders`
2. Regarder les premières lignes des données. Identifier la différence avec
un DataFrame standard.
3. Afficher l'attribut `crs` de `communes_borders`. Ce dernier contrôle la
transformation de l'espace tridimensionnel terrestre en une surface plane.
Utiliser `to_crs` pour transformer les données en Lambert 93 (code EPSG 2154).
4. Afficher les communes des Hauts de Seine (département 92) et utiliser la méthode
`plot`
5. Réprésenter la carte de Paris : quel est le problème ?
```{=html}
</div>
```
:::
```{python}
#1) Chargement des données de Cartiflette
communes_borders = cartiflette.s3.download_vectorfile_url_all(
values = ["75", "92", "93", "94"],
level="COMMUNE",
vectorfile_format="geojson",
decoupage="departement",
year=2022)
```
```{python}
# 2) Regarder les premières lignes
communes_borders.head()
# Il y a une colonne geometry qui contient les informations nécessaires pour connaître les contours communaux
```
```{python}
# 3) Afficher le crs
communes_borders.crs
# Les données sont en WGS84, on les reprojette en lambert 93
```
La carte du 92 est la suivante:
```{python}
# 4) afficher les communes du département 92
ax = communes_borders[communes_borders['INSEE_DEP'] == "92"].plot()
ax.set_axis_off()
```
Quant à Paris, à l'issue de la question 5, la carte
aura l'aspect suivant:
```{python}
# 5) Représenter la carte de Paris. Quel est le problème ?
ax = communes_borders[communes_borders['INSEE_DEP'] == "75"].plot()
ax.set_axis_off()
```
En effet, on ne dispose ainsi pas des limites des arrondissements parisiens, ce
qui appauvrit grandement la carte de Paris.
On pourrait les récupérer directement
depuis le site d'_open-data_ du Grand Paris, ce qui est proposé
en [exercice supplémentaire 👇️](#exo-supp).
On propose ici d'utiliser à nouveau
`cartiflette` pour cela afin de disposer du fonds de carte officiel.
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice 2: compléter des données spatiales issues de sources différentes</h3>
```
1. Importer les données de découpage des arrondissements parisiens à l'adresse à l'aide de `cartiflette`.
2. Vérifier sur une carte que les découpages des arrondissements sont bien présents.
3. Vérifier l'attribut `crs`. Est-il cohérent avec celui des données communales ?
Si non, transformer en Lambert 93 (code EPSG 2154).
4. Retirer Paris du jeu de données communales et utiliser les arrondissements
pour enrichir (nommer l'objet obtenu `data_borders`).
5. Représenter à nouveau les communes de la petite couronne parisienne (75, 92, 93, 94)
```{=html}
</div>
```
:::
```{python}
#1) Importer arrondissements
arrondissements = cartiflette.s3.download_vectorfile_url_all(
values = "75",
level="ARRONDISSEMENT_MUNICIPAL",
vectorfile_format="geojson",
decoupage="departement",
year=2022)
```
La carte de Paris intra-muros est, après la
récupération des arrondissements avec
`cartiflette` de ce type là:
```{python}
#2) Vérifier présence arrondissements
ax = arrondissements.plot(alpha = 0.8, edgecolor = "k")
ax.set_axis_off()
```
```{python}
#3) Vérifier l'attribut `crs`. Est-il cohérent ?
print(communes_borders.crs)
print(arrondissements.crs)
arrondissements = arrondissements.to_crs(2154)
print(communes_borders.crs == arrondissements.crs)
```
```{python}
#4) Retirer Paris et ajouter les arrondissements
import pandas as pd
data_paris = pd.concat(
[
communes_borders[communes_borders['INSEE_DEP'] != "75"].to_crs(2154),
arrondissements.to_crs(2154)
])
```
La carte obtenue à l'issue de la question 6, c'est-à-dire après
avoir consolidé les données, devrait avoir l'aspect suivant:
```{python}
#5) Représenter les communes du 75, 92, 93, 94
ax = data_paris.plot(alpha = 0.3, edgecolor = "k")
ax.set_axis_off()
```
# Utiliser des données géographiques comme des couches graphiques
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](https://opendata.paris.fr/explore/dataset/velib-emplacement-des-stations/table/) 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>
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice 3: importer et explorer les données velib</h3>
```
1. Importer les données velib sous le nom `station`
2. Vérifier la projection géographique de `station` (attribut `crs`). Si celle-ci est différente des données communales, reprojeter ces
dernières dans le même système de projection que les stations de vélib
3. Représenter sur une carte les 50 stations les plus importantes (variable `capacity`). Vous pouvez également afficher le fonds de carte des arrondissements de Paris.
Cette [page](https://geopandas.org/mapping.html#maps-with-layers) peut vous aider pour comprendre comment afficher plusieurs couches à la fois. Vous pouvez customiser la carte en retirant les axes grâce à la méthode `set_axis_off` et mettre un titre tel que _"Les 50 principales stations de Vélib"_ avec la méthode `set_title`.
4. Afficher également (trait bleu et épais) les réseaux de transport en communs, disponibles [ici](https://data.iledefrance-mobilites.fr/explore/dataset/traces-du-reseau-ferre-idf/map/?location=7,48.69717,2.33167&basemap=jawg.streets). L'url à requêter est
<https://data.iledefrance-mobilites.fr/explore/dataset/traces-du-reseau-ferre-idf/download/?format=geojson&timezone=Europe/Berlin&lang=fr>
```{=html}
</div>
```
:::
```{python}
# 1) Importer les données velib
url = "https://opendata.paris.fr/explore/dataset/velib-emplacement-des-stations/download/?format=geojson&timezone=Europe/Berlin&lang=fr"
stations = gpd.read_file(url)
stations.head()
```
```{python}
# 2) Reprojection
stations.crs
data_paris = data_paris.to_crs(stations.crs)
```
La carte attendu à l'issue de la question 3 a l'aspect suivant:
```{python}
# 3) Carte des 50 stations les plus importantes.
base = data_paris[data_paris['INSEE_DEP'] == '75'].plot(alpha = 0.2, edgecolor = 'black')
stations.sort_values('capacity', ascending = False).head(50).plot(ax = base, color = 'red', alpha = 0.6)
base.set_axis_off()
base.set_title("Les 50 principales stations de Vélib")
```
```{python}
# 4) Réseaux de transport en communs
url = "https://data.iledefrance-mobilites.fr/explore/dataset/traces-du-reseau-ferre-idf/download/?format=geojson&timezone=Europe/Berlin&lang=fr"
transports = gpd.read_file(url)
transports.head()
print(transports['mode'].unique())
```
L'ajout du réseau de métro permet d'obtenir une carte ressemblant à celle-ci:
```{python}
# 4) Réseaux de transport en communs (suite)
base = data_paris[data_paris['INSEE_DEP'] == '75'].plot(alpha = 0.2, edgecolor = 'black')
stations.sort_values('capacity', ascending = False).head(50).plot(ax = base, color = 'red', alpha = 0.6)
transports[transports['mode'] == "METRO"].plot(ax=base, color = 'blue', alpha = 0.3,linewidth=3)
base.set_axis_off()
base.set_title("Les 50 principales stations de Vélib")
```
Pour faire une belle carte, il faudrait couper les lignes de métro via une jointure spatiale ou
utiliser un fonds de carte conceptuel.
L'exercice suivant propose de mettre en oeuvre la deuxième méthode. La première
est proposée en [exercice supplémentaire 👇️](#exo-supp).
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice 4: ajouter un fond de carte</h3>
```
1. Recréer par couche successive la carte précédente, que vous pouvez nommer `base`
2. Utiliser `add_basemap` du package [`contextily`](https://contextily.readthedocs.io/en/latest/)
pour ajouter, en arrière plan, un fonds de carte
3. Jouer avec les fonds disponibles en utilisant l'argument `source`
```{=html}
</div>
```
:::
Par exemple, en utilisant le fond `Stamen.Watercolor`, on obtient la carte
suivante. Celle-ci permet déjà de mieux localiser les stations.
```{python}
base = data_paris[data_paris['INSEE_DEP'] == '75'].to_crs(3857).plot(alpha = 0.2, edgecolor = 'black')
stations.sort_values('capacity', ascending = False).head(50).to_crs(3857).plot(ax = base, color = 'red', alpha = 0.6)
base.set_axis_off()
base.set_title("Les 50 principales stations de Vélib")
ctx.add_basemap(base, source=ctx.providers.Stamen.Watercolor)
```
# Jointures spatiales
Les jointures attributaires fonctionnent comme avec un DataFrame `pandas`.
Pour conserver un objet spatial *in fine*, il faut faire attention à utiliser en premier (base de gauche) l'objet `GeoPandas`.
En revanche, l'un des intérêts des objets geopandas est qu'on peut également faire une jointure sur la dimension spatiale grâce à `sjoin`.
La documentation à laquelle se référer est [ici](https://geopandas.org/mergingdata.html#spatial-joins).
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice 5 : Associer les stations aux communes et arrondissements auxquels elles appartiennent</h3>
```
1. Faire une jointure spatiale pour enrichir les données de stations en y ajoutant des informations de `data_paris`. Appeler cet objet `stations_info`
2. Représenter la carte des stations du 19e arrondissement (s'aider de la variable `c_ar`). Vous pouvez mettre en fond de carte les arrondissements parisiens.
3. Compter le nombre de stations velib et le nombre de places velib par arrondissement ou commune (pour vous aider, vous pouvez compléter vos connaissances avec [ce tutoriel](https://pandas.pydata.org/docs/getting_started/intro_tutorials/06_calculate_statistics.html)). Représenter sur une carte chacune des informations
4. Représenter les mêmes informations mais en densité (diviser par la surface de l'arrondissement ou commune en km2)
5. (optionnel) Choisir une des cartes de densité et la nettoyer (retirer les axes, mettre les titres...)
```{=html}
</div>
```
:::
```{python}
#1. Jointure spatiale entre stations et data_paris
stations_info = gpd.sjoin(stations, data_paris, predicate = 'within')
stations_info.head()
```
Pour la question 2,
la première méthode consiste à afficher
toute la ville mais à ne représenter que
les points des stations du 19e:
```{python}
#2. Carte des stations du 19e arrondissement
# Méthode 1 : En affichant tout Paris
base = data_paris[data_paris['INSEE_DEP'] == "75"].plot(alpha = 0.2, edgecolor = 'k') #fond de carte des arrondissements
stations_19 = stations_info.loc[stations_info['NOM'].str.contains("19e")]
stations_19.plot(ax = base, color = 'red', alpha = 0.6) # stations du 19e
```
Néanmoins, il est préférable de se centrer sur
le 19e en premier lieu, ce qui donne une
carte comme celle-ci:
```{python}
# Méthode 2 : En affichant seulement le 19e
base = data_paris[data_paris['NOM'].str.contains("19e")].to_crs(3857).plot(alpha = 0.2, edgecolor = 'k') #fond de carte du 19e
stations_info[stations_info['NOM'].str.contains("19e")].to_crs(3857).plot(ax = base, color = 'red', alpha = 0.6) #stations du 19e
ctx.add_basemap(base, source=ctx.providers.Stamen.Toner)
```
```{python}
#3. Nombre de stations et de places vélib par arrondissement
stations_agg = stations_info.groupby('NOM').agg({'stationcode': 'nunique',
'capacity': 'sum'}).reset_index()
stations_agg.head()
df = data_paris.merge(stations_agg, how = 'inner')
df.head()
```
La carte des places disponibles est celle-ci:
```{python}
#3. Nombre de stations et de places vélib par arrondissement
ax = df.plot(column = 'capacity', legend=True)
ax.set_axis_off()
ax.set_title("Nombre de places disponibles")
```
Alors que la carte des capacités de stations est
plutôt celle-là:
```{python}
#3. Nombre de stations et de places vélib par arrondissement
ax = df.plot(column = 'stationcode', legend=True)
ax.set_axis_off()
ax.set_title("Nombre de stations")
```
Pas vraiment de différence marquée entre les
deux, on peut se contenter de regarder la capacité.
Enfin, dans la question 4,
si on représente plutôt la capacité
sous forme de densité, pour tenir compte
de la taille différente des arrondissements,
on obtient cette carte:
```{python}
#4. En densité
cols = ['stationcode','capacity']
df[[s + '_density' for s in cols]] = df[cols].div(df.to_crs(2158).area*10**(-6), axis = 0)
df.plot(column = 'capacity_density', cmap = 'RdYlBu_r', legend=True)
```
Avec une palette `plasma_r`, cela donne plutôt cette carte:
```{python}
#4. En densité
df.plot(column = 'capacity_density', cmap = 'plasma_r', legend=True)
```
Avec un peu de travail sur l'esthétique, la carte
que vous obtenez à l'issue de l'exercice
ressemble à celle-ci:
```{python}
#| output: false
# 5 Cartes nettoyées
ax = df.plot(column = 'capacity_density', cmap = 'RdYlBu_r', legend=True, legend_kwds={"orientation": "horizontal", "pad": 0.01})
ax.set_axis_off()
ax.set_title("Densité des stations vélib dans l'agglomération parisienne")
```
```{python}
ax.get_figure()
```
```{python}
#| echo: false
#| output: false
fig = ax.get_figure()
fig.savefig("featured.png")
```
# Trouver les toilettes publiques les plus proches
## Objectif
Jusqu'à présent, nous nous sommes familiarisés avec
la manipulation de données spatiales et la représentation
rapide de celle-ci grâce aux fonctionalités de `GeoPandas`.
A partir de maintenant, nous allons utiliser `GeoPandas`
pour des tâches de manipulation géométrique.
Ces opérations reposeront sur des tâches classiques
de la géomatique qui sont facilitées par le fait que
`GeoPandas` offre une surcouche au package `Shapely`
de la même manière que `Pandas` était une sur-couche
de `Numpy` pour les opérations numériques.
L'exemple suivant permet d'illustrer
le principe d'une des
opérations que nous allons utiliser,
à savoir la recherche de plus proche point:
```{python}
#| echo: true
from shapely.ops import Polygon
from shapely.ops import nearest_points
triangle = Polygon([(0, 0), (1, 0), (0.5, 1), (0, 0)])
square = Polygon([(0, 2), (1, 2), (1, 3), (0, 3), (0, 2)])
[o.wkt for o in nearest_points(triangle, square)]
```
`GeoPandas` va permettre de généraliser ce processus
en utilisant non plus deux listes modifiées (les
polygones de `Shapely`) mais des `DataFrames` géographiques.
Cela permettra, au passage, d'enrichir les
jointures spatiales avec les attributs des `DataFrames`
concernés.
Sur `Shapely`, vous pourrez trouver une aide [ici](https://pysal.org/scipy2019-intermediate-gds/deterministic/gds1-relations.html#how-about-nearest-neighbor-joins).
Néanmoins, à mesure que `GeoPandas` se développe, il
devient de moins en moins nécessaire d'utiliser directement
`Shapely`.
## Mise en application
Nous allons rechercher les toilettes publiques les
plus proches de chaque station.
Sans les fonctionalités de `GeoPandas`,
cette recherche serait assez pénible.
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice 5 (optionnel) : Trouver les toilettes publiques les plus proches d'une station de vélib</h3>
```
1. Charger la localisation des toilettes publiques présente ici : https://data.ratp.fr/explore/dataset/sanitaires-reseau-ratp/download/?format=geojson&timezone=Europe/Berlin&lang=fr. Appelez-la `toilettes_publiques`.
2. Convertir les objets `toilettes_publiques` et `stations` en projection Lambert-93 (CRS 2154). Cette
conversion permettra de mesurer en mètres les distances entre objets géographiques. Sans
celle-ci, nous ferions des distances entre coordonnées GPS, ce qui n'aide pas l'analyse
et l'interprétation.
3. Utiliser la jointure spatiale par plus proche distance `sjoin_nearest` pour associer à chaque station les toilettes publiques les plus proches
4. Trouver les toilettes publiques les plus proches des stations de vélib autour d'Edgard Quinet.
5. Représenter un histogramme des distances aux toilettes les plus proches
```{=html}
</div>
```
:::
Le jeu de données _open-data_ des toilettes
publiques présente l'aspect suivant:
```{python}
# 1. Charger les localisations des toilettes
toilettes_publiques = gpd.read_file("https://data.ratp.fr/explore/dataset/sanitaires-reseau-ratp/download/?format=geojson&timezone=Europe/Berlin&lang=fr")
toilettes_publiques.head(2)
```
```{python}
#2. Conversion des CRS
toilettes_publiques = toilettes_publiques.to_crs(2154)
stations = stations.to_crs(2154)
```
Les toilettes les plus proches
d'Edgar Quinet sont les suivantes:
```{python}
# 3. Jointure spatiale
stations_toilettes = stations.sjoin_nearest(
toilettes_publiques,
how='inner',
distance_col="distance")
# 4. Trouver les toilettes les plus proches d'Edgard Quinet
stations_toilettes.loc[
stations_toilettes['name'].str.contains("Edgar Quinet"),
["localisation", "station","name","distance"]]
```
Il va donc falloir se
retenir un peu car s'agit de toilettes situées
à la station Denfert Rochereau !
Enfin, de manière plus globale, voici la distribution
des distances aux toilettes les plus proches:
```{python}
#5. Histogramme
stations_toilettes['distance'].plot(kind = "hist")
```
Le mode de la distribution est entre 1 et 2 km, ce
qui est une petite distance tout de même !
C'est normal, il ne s'agit pas de l'ensemble des
toilettes publiques de la ville de Paris mais
de celles gérées par la RATP. Rassurez-vous, au
moins dans Paris intra-muros, vous n'avez pas
à systématiquement marcher (ou rouler) autant.
# Exercices supplémentaires {#exo-supp}
Voici une fonction pour télécharger et dézipper
facilement un fonds de carte issu de `data.gouv`
```{python}
#| echo: true
#| include: true
import requests
import tempfile
import zipfile
temporary_location = tempfile.gettempdir()
def download_unzip(url, dirname = tempfile.gettempdir(), destname = "borders"):
myfile = requests.get(url)
open("{}/{}.zip".format(dirname, destname), 'wb').write(myfile.content)
with zipfile.ZipFile("{}/{}.zip".format(dirname, destname), 'r') as zip_ref:
zip_ref.extractall(dirname + '/' + destname)
```
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice optionnel 1: télécharger et dézipper vous-même le fonds de carte</h3>
```
Importer le fichier avec le package `GeoPandas`
(si vous avez laissé les paramètres par défaut,
le fichier devrait
être à l'emplacement `temporary_location + "/borders/communes-20210101.shp"`).
```{=html}
</div>
```
:::
```{python}
#| eval: false
# 1) télécharger les données communales
url = "https://www.data.gouv.fr/fr/datasets/r/0e117c06-248f-45e5-8945-0e79d9136165"
download_unzip(url)
```
```{python}
#| eval: false
# 2) Importer le fichier
#communes_borders = gpd.read_file(temporary_location + "/borders/communes-20220101.shp")
```
::: {.cell .markdown}
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice optionnel 2 : Utiliser les arrondissements fournis par l'open data parisien</h3>
```
1. Importer les données de découpage des arrondissements parisiens à l'adresse
<https://opendata.paris.fr/explore/dataset/arrondissements/download/?format=geojson&timezone=Europe/Berlin&lang=fr>
2. Vérifier sur une carte que les découpages des arrondissements sont bien présents.
3. Vérifier l'attribut `crs`. Est-il cohérent avec celui des données communales ?
4. Retirer Paris du jeu de données communales et utiliser les arrondissements
pour enrichir (nommer l'objet obtenu `data_borders`). Ici, on peut ne pas se
soucier de la variable commune de superficie aux niveaux différents car on
va la recréer. En revanche, renommer la variable `c_arinsee` en `insee` avec
la méthode `rename` et faire attention aux types des variables
```{=html}
</div>
```
:::
```{python}
#1) Importer arrondissements
arrondissements = gpd.read_file("https://opendata.paris.fr/explore/dataset/arrondissements/download/?format=geojson&timezone=Europe/Berlin&lang=fr")
arrondissements = arrondissements.to_crs(2154)
```
```{python}
#2) Vérifier présence arrondissements
arrondissements.plot()
```
```{python}
#3) Vérifier l'attribut `crs`. Est-il cohérent ?
print(communes_borders.crs)
print(communes_borders.crs == arrondissements.crs)
# Oui, les deux fonds de carte ont le même attribut CRS, pas besoin de convertir les projections de l'une d'entre elles
```
```{python}
#4) Retirer Paris et ajouter les arrondissements
## On fait en sorte que les colonnes de arrondissements soient les mêmes que celles de communes borders
arrondissements = arrondissements.rename(columns = {"c_arinsee": "INSEE_DEP"})
arrondissements['INSEE_DEP'] = "75"
# On sélectionne enlève la ligne du contour de Paris initiale, et on ajoute à la place les lignes d'arrondissements
data_paris = communes_borders[
communes_borders["INSEE_DEP"] != "75"].to_crs(2154).append(arrondissements.to_crs(2154))
data_paris.tail(2)
```
## Jointures spatiales
L'objectif de cet exercice est de ne conserver que les
lignes de transports à l'intérieur de Paris intra-muros.
Il s'agit d'appliquer les jointures spatiales de manière
un petit peu différente à précédemment.
```{=html}
<div class="alert alert-success" role="alert">
<h3 class="alert-heading"><i class="fa-solid fa-pencil"></i> Exercice optionnel 3 : Les lignes de transport dans Paris</h3>
```
1. Utiliser l'URL <https://data.iledefrance-mobilites.fr/explore/dataset/traces-du-reseau-ferre-idf/download/?format=geojson&timezone=Europe/Berlin&lang=fr> pour récupérer les lignes de transport
de la RATP. L'appeler `transports`.
2. A partir des arrondissements parisiens, utiliser `unary_union` pour créer un unique polygone parisien. Utiliser `within` pour ne conserver que les points de `transports` qui se trouvent
dans Paris intra-muros
3. Représenter graphiquement
```{=html}
</div>
```
:::
```{python}
url = "https://data.iledefrance-mobilites.fr/explore/dataset/traces-du-reseau-ferre-idf/download/?format=geojson&timezone=Europe/Berlin&lang=fr"
transports = gpd.read_file(url)
```
```{python}
#2)
paris = data_paris.loc[data_paris['INSEE_DEP']=="75"].geometry.unary_union
transports_paris = transports[transports.geometry.within(paris)]
```
La carte obtenue aura l'aspect suivant:
```{python}
base = data_paris[data_paris['INSEE_DEP'] == '75'].plot(alpha = 0.2, edgecolor = 'black')
transports_paris.plot(ax=base, column='mode', alpha = 0.9, linewidth=3)
```
Cette fois, on a bien conservé que les lignes de transport dans
Paris. Un peu de travail sur le rendu serait nécessaire pour
obtenir une belle carte. Vous pouvez le faire en exercice, après
avoir consulté le chapitre relatif à la cartographie dans
la partie visualisation de données.