<figure><img src="image_46.png" /><figcaption>FIGURE 14.3 – Carte des séismes ($M \ge 1$) des 7 derniers jours. Source base de données de l'USGS</figcaption></figure>

```python
#save to html file
macarte.save('f_3.html')
```

## 14.5 Les clusters

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

Created on Mon May 18 09:16:23 2020

@author: metivier

folium maps

using Cluster Markers to plot the place where students where
the year before registering at IPGP

"

import folium
from folium.plugins import MarkerCluster

fromimport create_engine
import pandas as pd
import numpy as np

# create the map
macarte = folium.Map(location=[46.5,2.5], zoom_start=6)

engine = create_engine("mysql://login:password@localhost/Parcours")

sql = "select cp, prenom from IdentiteTbl i inner join ParcoursTbl p on i.idetudiant = p.idetudiant where annee=anneeufr-1 and pays='France'"

stud = pd.read_sql/sql, engine)

f=open('../include/data/laposte_hexasmal_1.csv')
```
<figure><img src="image_47.png" /><figcaption>FIGURE 14.4 – Localité des étudiants un an avant leur arrivée en Licence ST</figcaption></figure>

```python
line=f.readline()
lines=f.readlines()

codep=[]
lat=[]
lon=[]

for l in lines:
    d=l.split(',')
    codep.append(d[2])
    lat.append(d[5])
    lon.append(d[6].strip("
"))

data={'cp':codep, 'lat':lat, 'lon':lon}

cp = pd.DataFrame(data, columns=['cp', 'lat', 'lon'])
ccp= cp.drop_dupliicates('cp')

merged=stud.set_index('cp').join(ccp.set_index('cp'))
merged=merged.dropna()

mc = MarkerCluster()
for index, row in merged.itertrows():
    mc.add_child(folium internal. Marker(location=[row['lat'], row['lon']], popup = row['prenom'])

macarte.add_child(mc)

macarte.save('f_4.html')
```

## 14.6 GeoJSON

Folium est une librairie qui permet de transformer vos instructions en instructions javascript de la librairie cartographique leafletjs. Le format standard d'échange et de gestion de données en javascript s'appelle JSON (Javascript Object Notation). Il possède deux versions adaptées aux SIG le GeoJSON et le TopoJSON qui prennent en charge les géométries. la différence entre ces deux libraries tient au fait que TopoJSON permet d'éviter la redondance des tracés. Ainsi, dans un fichier TopoJSON la frontière entre le Canada et les États-Unis ne sera enregistrée qu'une fois alors qu'elle le sera deux fois dans un fichier GeoJSON. Du fait de l'utilisation de JSON comme format de données il n'est pas possible de représenter directement un shapefile avec folium. Il
faut d'abord passer par une étape de conversion des données au format GeoJSON. Le script `ShapeTransform.py` montre une façon de procéder à partir des libraires `osgeo` et `json`. La fonction `shape_togeojson` retourne un objet JSON et un `DataFrame` Pandas à partir d'un fichier shapefile

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

1
2
3
4 Library to transform shapefiles
5
6 import osgeo.ogr
7 import json
8 import pandas
9
10
11 def shape_to geojson (fname):
12     """
13     reads a shapefile and returns
14       1) a geosjson object
15       2) a pandas dataframe
16     """
17
18 #get the shapefile
19 shapefile = osgeo.ogr.Open (fname)
20 layer = shapefile .GetLayer (0)
21 layerdefn = layer .GetLayerDefn ()
22 FieldList = []
23 FieldType = []
24 dfDic = {}
25 for i in range (layerdefn .GetFieldCount ()):
26     FieldName = layerdefn .GetFieldDefn (i) .GetName ()
27     FieldList.append (FieldName)
28     dfDic[ FieldName ] = []
29
30 numFeatures = layer .GetFeatureCount ()
31
32 #initialize the Geojson dic
33 data ={ "type" : "FeatureCollection", "features" : []}
34
35 jd = json.JSONDecoder ()
36
37 #loop through the data
38 for featureNum in range (numFeatures):
39     feature = layer .GetFeature (featureNum)
40
41
42 #first add to the Geojson dic
43 fdic ={ }
44 fdic [ "type" ] = "Feature"
45 attributes = feature.items ()
46
47 #create the properties dic
48 pdic ={ }
49 for key, val in attributes.items ():
50     pdic[key] = val
51     fdic [ "properties" ] = pdic
52
53 #create the geometry dic
54 geometry =feature .GetGeometryRef ()
55 # we must first export to json then Transform into a dic
56 fdic [ "geometry" ] = jd.decode (geometry .ExportTojson())
57
58 #add the feature to the featurecollection dic
59 data [ "features" ].append (fdic)
60
61 #second add to the data columns
62 for Field in FieldList:
63     dfDic[ Field].append (attributes [Field])
```
64

65

66

67

68

69

70

71

72

73

74

75

```python
df = pandas.DataFrame(dfDic)
```

```python
return json.dumps(data), df
```

```python
if __name__ == '__main__':
```

```python
data, df = shape_to_geometry(df, '../../../map/include/shapefiles/ne_50m_admin_0_countries/ne_50m_admin_0_countries.shp')
```

```python
print(df head())
```

```python
print(df.columns)
```

Une fois cette conversion faite il devient alors possible de représenter nos données sur une carte en utilisant différentes méthodes de folium.

```python
#!/usr/bin/env python3
```

```python
# -*- coding: utf-8 -*-
```

```python
"
```

```python
@author: metivier
```

```python

```

```python
folium maps
```

```python

```

```python
Transform plot GeoJON polygons
```

```python
and add information on Popups
```

```python
10
```

```python
11
```

```python
12
```

```python
13
import folium
```

```python
14
import numpy as np
```

```python
15
import pandas
```

```python
16
```

```python
17
from ShapeTransform import shape_to_geometry
```

```python
18
```

```python
def big_style(x):
```

```python
"
```

```python
samples tyle function
```

```python
"
```

```python
if x['properties'][ 'POP_EST'] <= 1e6 :
```

```python
        stdic = {'fillColor': 'darkgreen', "color":"black", "opacity":0.4}
```

```python
elif x['properties'][ 'POP_EST'] <= 1e7 and x['properties'][ 'POP_EST'] > 1e6:
```

```python
        stdic = {'fillColor': 'green', "color":"black", "opacity":0.4}
```

```python
elif x['properties'][ 'POP_EST'] <= 1e8 and x['properties'][ 'POP_EST'] > 1e7:
```

```python
        stdic = {'fillColor': 'yellow', "color":"black", "opacity":0.4}
```

```python
elif x['properties'][ 'POP_EST'] <= 1e9 and x['properties'][ 'POP_EST'] > 1e8:
```

```python
        stdic = {'fillColor': 'orange', "color":"black", "opacity":0.4}
```

```python
elif x['properties'][ 'POP_EST'] > 1e9:
```

```python
        stdic = {'fillColor': 'red', "color":"black", "opacity":0.4}
```

```python
return stdic
```

```python
35 macarte = folium.Map(location=[46.5,2.5], zoom_start=3)
```

```python
data, df = shape_to_geometry( '../../../map/include/shapefiles/ne_50m_admin_0_countries/ne_50m_admin_0_countries.shp')
```

```python
38
```

```python
39 #add some information because it's a mess to get the legend
```

```python
40 JsonLayer = folium.GeoJson(data,style_function=big_style,name='Population')
```

```python
41 JsonPop = folium features.GeoJsonPopup (fields=['NAME','POP_EST'], aliases = ["Country", "Population"]).add_to (JsonLayer)
```

```python
42
```

```python
43
```

```python
44
```

```python
45
folium LayerControl() .add_to (macarte)
```

```python
46
macarte.save('f_5.html')
```
<figure><img src="image_48.png" /><figcaption>FIGURE 14.5 – Population mondiale première version : GeoJson + GeoJsonPopup</figcaption></figure>

14.7 Choroplèthes

Les Choroplèthes (Choropleth) sont des cartes dont certaines régions sont teintées en fonction des valeurs
prises par une variable. Une carte de France dont les départements sont coloriés en fonction, par exemple, de
leur population est une Choroplèthe. La figure 14.5 que nous venons de faire est une Choroplète tout comme
les figures 11.3 et 13.1. folium propose une fonction Chloropleth qui permet de représenter des données en
provenance d'un DataFrame Pandas sur une géometrie GeoJSON. Le script suivant montre comment s'en servir.
L'avantage de cette fonction est qu'elle gère aussi la représentation de la légende et qu'elle est assez simple à
mettre en oeuvre.

1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 """
4 @author: metivier
5
6 folium maps
7
8 Plot Choropleth from a shapefile
9
10
11 """
12
13 import folium
14 import matplotlib.cm as cm
15 from matplotlib_colors import rgb2hex
16 import numpy as np
17 import pandas
18
19 from ShapeTransform import shape_to geojson
20
21 macarte = folium.Map(location=[46.5,2.5], zoom_start=3)
22
23 data, df = shape_to geojson( '../../map/include/shapefiles/ne_50m_admin_0_countries/ ne_50m_admin_0_countries.shp' )
24
25 # Populations extends over orders if magnitude so we need to
26 # prepare the data a little
27 df = df.fillna(value={ 'POP_EST': 1}) # get rid of nans
<figure><img src="image_49.png" /><figcaption>FIGURE 14.6 – Population mondiale seconde version ; Choropleths</figcaption></figure>

```python
df = df.replace({'POP_EST': 0}, 1)  # replace 0 values
df['POP_EST'] = np.log10(df['POP_EST'])  # go to log10 scale

# In the end plot
cp = folium.Choropleth(data, df, columns=['ISO_A3', 'POP_EST'], key_on='feature.
                                      properties.ISO_A3',
                                      fill_color='PuRd', name='Population', legend_name='Populaton (log scale)').add_to(
                                                                 macarte)

folium LayerControl().add_to(macarte)
macarte.save('f_6.html')
```

## 14.8 Ajouter des Traçés

Les deux exemples suivant montrent comment utiliser des données de position pour tracer des courbes. Dans le premier cas les données sont dans une base de données MySQL qui recense les ouragans de l'atlantique. Dans le second cas les données proviennent d'un shapefile dans lequel on trouve les isopièzes de la nappe des Landes.

### 14.8.1 Trajectoires d'ouragans

Les ouragans sélectionnés sont ceux de 2018. Attention si vous sélectionnez tout vous risquez d'avoir une carte très lourde...

```python
1 ###########################################
2 # librairies
3 ###########################################
4
5 import folium
6 fromsqlacalcymyimport create_engine
7
8 ###########################################
9 # create the map
10 ###########################################
11 macarte = folium.Map(location=[10,-20], zoom_start=3)
12
13 ###########################################
14 # connexion
15 ###########################################
```
<figure><img src="image_50.png" /><figcaption>FIGURE 14.7 – Trajectoire des ouragans de 2018.</figcaption></figure>

```python
engine = create_engine("mysql://login:password@localhost/hurricanes")
conn = engine.connect()

sql = "select distinct code, name from reference where code regexp '2018'"
res = conn.execute/sql(
    for line in res[:5]
    # print code
    print(line[0])
    sql = "select latitude, longitude from data where code = %s"
    pos = conn.execute/sql, (line[0])
    points = []
    for p in pos:
        if p[0)[-1] == 'N':
            lat = float(p[0][:-1])
        else:
            lat = -float(p[0][:-1])
        if p[1)[-1] == 'E':
            lon = float(p[1][:-1])
        else:
            lon = -float(p[1][:-1])
    points.append((lat, lon))
    folium CircleMarker(points[0], radius=3, popup=line[0] + '<br>' + line[1]).
    add_to macarte
    folium.PolyLine(points, color='red').add_to macarte

#save to html file
macarte.save('f_7.html')
```

## 14.8.2 Carte piézométrique

Deux petites fonctions sont crées afin de (1) faciliter la recherche des hauteurs piézométrique min et max et (2) inverser les coordonnées lon/lat des shapefiles en lat/lon pour folium.

```bash
#!/usr/bin/env python3
```
2 # -*- coding: utf-8 -*-

3 """

4 @author: metivier

5

6 folium maps

7

8 Plot Choropleth from a shapefile

9

10

11 """

12 ########################################

13 # Librairies

14 ########################################

15 import folium

16 import numpy as np

17 import cartopy.io.shapereader as shreader

18 import branca.colormap as cm

19

20

21 ########################################

22 # extract extent of a shapefile attribute

23 ########################################

24

25 def getIRETENT (shapefile, attribute):

26 reader = shreader .Reader (shapefile)

27 features = reader.records()

28

29 min_val = 9e19

30 max_val = -9e19

31 for feature in features:

32 min_val =min (min_val,feature .attributes [attribute]))

33 max_val =max (max_val,feature .attributes [attribute]))

34

35 return min_val, max_val

36

37 ########################################

38 # transform linestring to folium polyline ada

39 ########################################

40 def LineString_to_folium dat( idat ):

41 odat =[(t[1],t[0]) for t in idat]

42 return odat

43

44 ########################################

45 # create the map

46 ########################################

47

48 macarte =folium.Map (location=[44.5,0], zoom_start=8)

49

50 ########################################

51 # read shapefiles

52 ########################################

53

54 filename = ’../../map/include/shapefiles/Aquitaine/Isopiezes_mio-plio- quaternaire_latlon.shp’

55 reader =shreader .Reader(filename)

56 features =reader.records()

57

58

59 ########################################

60 # create colormap

61 ########################################

62

63 cote_min,cote_max =getIRETENT(filename,’COTE_mNGF’)
<figure><img src="image_51.png" /><figcaption>FIGURE 14.8 – Carte piézométrique de la nappe des Landes</figcaption></figure>

```python
colormap caption = "Piezometric height (m above NGF)"
macarte.add_child(colormap)

」と same

70

71 # loop to transform geometries into linestrings
72 # for folium.PolyLine then plot
73
74 for feature in features:
75     cote = feature_attributes["COTE_mNGF"]
76     pp = "cote ▽ NGF: ▽ %f ▽ m" % cote
77     col = colormap( cote )
78
79 if feature_geometry geom_type == 'LineString':
80     dat = LineString_to_folium_dat( feature_geometry
81     folium.PolyLine( dat, popup = pp, color = col ).add_to( macarte)
82 else:
83     for line in feature_geometry:
84         dat = LineString_to_folium_dat( line coordinates )
85         folium.PolyLine( dat, popup = pp, color = col ).add_to( macarte)
86
87
88
89 # finished !
90
91
92 macarte.save('f_8.html')
```
Annexes
<figure><img src="image_52.png" /></figure>
# Annexe A

## Rappels Python

### A.1 Introduction

Cette section regroupe quelques rappels de ce que vous devez savoir pour la suite de ce cours. Ce n'est pas un cours de python ! Pour cela consultez les cours d'E. Gayer en L2 ou le cours de python donné en sciences du vivant à l'université Paris Diderot <fnref n="1" />.

Python est un langage de programmation qui permet de faire réaliser par l'ordinateur des opérations très diverses des plus simples aux plus complexes. Comme tout langage il repose sur une syntaxe et un vocabulaire. Contrairement aux langues parlées, la syntaxe est strict (l'ordinateur n'essaiera pas de vous comprendre si vous faites des fautes de grammaire), et le vocabulaire de départ limité. Mais ce vocabulaire peut être étendu à l'infini par l'intermédiaire de suites d'instructions regroupées dans ce qu'on nomme des fonctions ou des objets.

Python est un langage interprété, l'ordinateur exécute au fur et à mesure qu'il découvre ce qu'il lit. Les instructions que vous allez donner seront écrites de façon séquentielles. Ces instructions seront soit saisies directement dans l'interpréteur python, soit écrites dans un fichier texte (dont le nom finit avec l'extension .py) puis exécutées par l'interpréteur.

Comme pour MySQL nous allons utiliser un environnement simple et «brut» pour travailler. Celui-ci sera constitué de l'interpréteur et d'un éditeur de texte simple.

Lancer l'interpréteur en tapant `python` dans une console. Vous verrez le prompt changer en >>>. Vous êtes «dans» python. Commençons.

Dans le programme ci-dessous nous demandons à python d'effectuer qutre opérations : deux créations de variables auxquelles nous affectons une valeur entière et deux calcul que nous enregistrons dans deux autres variables <fnref n="2" />.

1 >>>a=2 # affecte la valeur deux a une variable a
2 >>>b=3 # affecte la valeur trois a une variable b
3 >>>c=a+b # affecte la somme de a et b a une variable c
4 >>>d=a/b # affecte la division de a par b a une variable d
5 >>>c
6 5
7 >>>d
8 0

Ici les variables sont des nombres entier. Python effectuera donc des opérations entières, à moins que vous ne lui spécifiez. Par conséquent on aura $d = a/b = 0$. Pour passer d'un entier à un réel et obtenir la division de deux nombres réels il suffit de faire

1 >>>d=1.0*a/b

Multiplier par 1.0 transforme le calcul, initialement sur des nombres entiers, en calcul sur des nombres réels. On verra que l'on a d'autres façon de procéder par la suite.

Sur la syntaxe, 1 ligne = 1 instruction. On verra que le langage permet en fait de faire plus d'une chose par ligne de code mais pour l'instant restons en à cette idée simple et vrai dans la majorité des cas. Le vocabulaire quand à lui est composé de de «mots». Les mots réservés du langage sont en nombre limité :=, les opérateurs, if, then, else, for, while etc...

Considérons deux points sur un axe muni d'une origine et d'un repère. On veut calculer la distance qui sépare les deux points.

<footnote n="1">1. https://python.sdv.univ-paris-diderot.fr</footnote>

<footnote n="2">2. Les fautes d'accentuations sont volontaires ! Le paquet latex que j'utilise pour les listing ne les aime pas...</footnote>
1 >>> x1=2
2 >>> x2=3
3 >>> if x1>x2:
4 ...     distance=x1-x2 # ne pas oublier d'indenter
5 ... else:
6 ...     distance=x2-x1
7 ...
8 >>> distance
9 1

On a introduit ce que l'on appelle une condition d'exécution. la distance est donnée par valeur absolue de la différence des deux positions. il faut donc tester quel est le nombre le plus grand et faire la «bonne» différence, telle que le résultat est positif. On notera que les calculs exécutés sous conditions sont indentés par une tabulation.

Deux défauts : x1 et x2 sont fixés et si on veut le faire pour deux autres valeurs il faut tout réécrire. Qu'à cela ne tienne les fonctions sont là pour nous

1 >>>def distance1D(x1,x2): # x1 et x2 sont des arguments
2 ...     if x1>x2:
3 ...         distance=x1-x2
4 ...     else:
5 ...         distance=x2-x1
6 ...     return distance
7 ...

On peut maintenant appeler notre fonction

1 >>>distance1D(3,8)
2 >>>5

Bon vous venez vaille que vaille d'écrire cinq lignes non ? Changeons de technique. lancer gedit et écrire le code dans un fichier que l'on exécutera ensuite. On en profitera pour utiliser une bilbiothèque de fonctions fort utile qui simplifiera grandement l'écriture.

1 # une ligne de commentaire : mon beau programme
2 # fichier distance1D.py
3 import numpy as np
4 def distance1D(x1,x2):
5     # calcul de la distance sur un axe muni de son repere norme
6     return np.abs(x1-x2)

Bon ben j'ai le programme il est beau et je l'exécute comment ? une première façon consiste à rajouter l'appel de la fonction sous sa définition

1 # une ligne de commentaire : mon beau programme
2 # fichier distance1D.py
3 import numpy as np
4 def distance1D(x1,x2):
5     # calcul de la distance sur un axe muni de son repere norme
6     return np abs(x1-x2)
7
8
distance1D(2,7)

puis dans une fenêtre vous exécutez

1 > python distance1D.py

Une seconde façon bien plus propre consiste à placer l'appel dans une condition «bizarre»

1 # une ligne de commentaire : mon beau programme
2 # fichier distance1D.py
3 import numpy as np
4 def distance1D(x1,x2):
5     # calcul de la distance sur un axe muni de son repere norme
6     return np abs(x1-x2)
7
8
9 if __name__ == '__main__':
10 distance1D(2,7)

Puis lancer le scripts de la même façon pour obtenir le même résultat. Idiot ? Voire.
## A.2 Structure d'un programme

### A.2.1 Libraries

Les bibliothèques sont des séries de fonctions ou de classes stockées dans des fichiers. ces fichiers peuvent à leur tour être stockés dans des répertoires. Vous devez déclarer les bibliothèques que vous devez utiliser en premier

```python
1 #structure
2 #import needed libraries
3 # you have to choose one way OR the other not both
4
5 # first way NOT RECOMMENDED
6 from matplotlib.pylab import *
7
8 # second way RECOMMENDED
9 import matplotlib.pylab as py
10
11 # in the first case you call a function from the pylab library by
12 x=arange(10)
13 #in the second case by
14 x=py.arange(10)
```

Les plus importantes librairies seront

- ` numpy`, `scipy` : pour le calcul scientifique

- ` matplotlib` : (`pylab`, `pyplot`) pour de très beaux graphiques

- `MySQLdb`, `pandas`, `sqlálchemy` : Pour se connecter à un serveur de données

- `basemap`, `pyshp`, `pyproj`, `shapely`, `geopandas` pour la cartographie

NOTE IMPORTANTE : `pylab` est une bibliothèque qui inclut toutes les fonctionnalités de traçage de `pyplot` + ` numpy` il «ressemble» donc à MATLAB.

### A.2.2 functions

```python
1 #define the function
2 def square(x):
3     return x*x
4
5 #then use it Note the indentation !
6 y=square(2)
7 print y
```

### A.2.3 classes

Les classes sont des objets abstraits en Python. Ce sont essentiellement des conteneurs qui ont des propriétés ou des fonctionnalités (ils peuvent effectuer des tâches).

```python
class person:
    def __init__(self, name="bobo", age="0"):
        self.name=name
        self.age=age
        self.talk()

    def speak(self):
        print "my %s is %s" % self.name
        print "I am %s years old" % self.age

# use the class
levieux=person("Francois",47)
levieux.speak()
```

Quand vous écrivez `levieux=person("Francois",47)` vous créez une instance de la classe `person`, un peu à la façon dont vous créerez un enregistrement dans une table. Cette instance (`Levieux`) hérite des propriétés et des fonctions (que l'on appelle des méthodes) de la classe `person`. De nombreuses bibliothèques de python sont organisées en classes.
<figure><img src="image_53.png" /><figcaption>FIGURE A.1 – $x \cos(x)$</figcaption></figure>

self signifie que l'objet fait référence à l'instance de lui-même en cours. Les variables de l'objet sont ainsi notées self variable et toute fonction de l'objet commence par faire référence à lui-même dans ses arguments.

## A.3 Exemples

### A.3.1 Premier graphique

Dans ce premier exemple, nous définissons un vecteur x qui va de 0 à 100 avec un pas de 0.1. Nous calculons ensuite $y = x \cos(x)$ puis nous représentons le résultat (Fig. A.1).

```python
1 # -*- coding: utf-8 -*-
2 """
3 pm_A1.py
4
5 plox xcos(x)
6 """
7 #import needed libraries
8 import matplotlib.pylab as py
9
10 #define the data
11 x=py.linspace(0,10,100)
12 y=x*py.cos(x)
13
14 #plot the result
15 py.figure() #define the figure
16 py.plot(x,y,'r-') #make the plot
17 pyanstatus=14) #add labels
18 pyylabel('y',fontsize=14)
19 py.legend(['$y=x\cos(x)$'], loc="upper left") # add legend including LaTeX commands
20 py.savefig("../figures/pm_A1.pdf", bbox_inches='tight') # save figure to file
21 py.show() # show the figure
```

### A.3.2 Surface libre d'une nappe

Dans ce deuxième exemple, nous souhaitons tracer la surface libre, en régime stationnaire, d'une nappe libre dans un aquifère 1D de conductivité hydraulique $K$. L'eau entre à un flux $q$ constant, au point $x = 0$ et de longueur $L$. La deuxième condition limite est une hauteur fixe de la nappe $h = 0$.
<figure><img src="image_54.png" /><figcaption>FIGURE A.2 – Profils d'une nappe en état stationnaire pour différentes conductivités hydrauliques.</figcaption></figure>

Le problème est alors défini par

$$ \nabla^2 h^2 = 0 \qquad (A.1) $$

$$ -Kh\partial_x h(0) = q \qquad (A.2) $$

$$ h(L) = 0 \qquad (A.3) $$

and the solution to (A.1), (A.2) and (A.3) is

$$ h = \sqrt{\frac{2q}{K}(L-x)} \qquad (A.4) $$

Dans le script suivant, nous dessinons la forme de la nappe phréatique pour un aquifère expérimental de 0,8 m de longueur et pour différentes conductivités hydrauliques. Le résultat est tracé sur la figure A.2

1 # -*- coding: utf-8 -*-

2 """

3 pm_A2.py

4

5 Solution of a steady 1D flow using Dupuit approximation over a length L

6 for different values of hydraulic conductivity K

7 Boundary conditions are a flux Q at 0 and zero depth y at 1.

8 """

9 # Import libraries

10 import matplotlib.pylab as py

11

12 # Define the problem

13 # constants

14 L = 0.8 # "true" length

15 Q = 1./6000 # discharge m2/s

16 K = [0.1, 0.075, 0.05, 0.025, 0.01] # hydraulic conductivity

17

18 # variable

19 x = py.linspace(0, L, 100) # x value array

20

21 # Prepare the figure

22 py.figure()

23 lgd = [] #legend

24

25 for k in K:
```python
y = py.square((2*Q/k)*(L-x)) # analytic solution for the water thickness
st = "$\sqrt{K} = %s\]$ % (k) # add the conductivity to the legend
lgd.append(st)
py.plot(x, y) #plot the result

# Figure labels, legend and axis
py.xlabel('x', fontsize = 14)
py.ylabel='$\sqrt{h} \sqrt{(2q/K)(L-x)}$ ', fontsize=14)
py.legend(lgd, loc = "upper right") # including LaTeX labels
py.axis([0, 0.8, 0, 0.2])

# save and display
py.savefig("../figures/pm_A2.pdf", bbox_inches = 'tight')
py.show()
```

## A.4 L'utilisation des axes

Le script suivant contient deux fonctions qui font appel à une libraire de matplotlib appelée **axes**. La première fonction fait littéralement la même chose que le script `pm_A1.py` vu au §A.3.1. La seconde plus élaborée permet de représenter deux fonctions $y = \cos(x)$ et $z = x^2$ sur la même figure à deux échelles différentes (figure A.3). À cette fin on représente les deux courbes sur deux systèmes d'axes différents. Ce genre de représentation peut s'avérer extrêmement utile. Elle nécessite de passer par la définition de deux axes pour une même figure.

```python
1 # -*- coding: utf-8 -*-
2 """
3 pm_A3.py
4
5 Using axes
6 """
7
8 #import needed libraries
9 import matplotlib.pylab as py
10
11 def xcosx():
12     #define the data
13     x = py.linspace(0, 10, 100)
14     y = x*py.cos(x)
15     y2 = x*py.sin(x)
16     #plot the result
17     fig = py.figure() #define the figure
18     ax = fig.add_subplot(211) #create the axes and add a subplot
19     ax2 = fig.add_subplot(212)
20     ax.plot(x, y, 'r-') #make the plot
21     ax2.plot(x, y2, 'g-')
22     ax.set_xlabel('x', fontsize = 14) #add labels
23     ax.set_ylabel('y', fontsize = 14)
24     ax.legend(["$y_\square=\square x_\square \cos(x)$"], loc = "upper left") # add legend including LaTeX commands
25     ax2.legend(["$y_\square=\square x_\square \sin(x)$"], loc = "upper left") # add legend including LaTeX commands
26     py.show()
27
28
29 def two_axes():
30     #define the data
31     x = py.linspace(0, 100, 1000)
32     y = py.cos(x)
33     z = x**2
34     #plot the results
35     fig2 = py.figure() #define the figure
36     ax21 = fig2.add_subplot(111) #create the axes and add a subplot
37     ax22 = ax21.twinx() # create a new y-axis but keep the same x-axis
38
39     ax21.plot(x, y, 'r-', label = '$y_\square=\square \cos(x)$') #make the plot
40     ax22semilogy(x, z, 'g-', label = "$y_\square=\square x^2$")
```
41

ax21.set_xlabel('x', fontsize = 14) #add labels

42

ax21.set_ylabel('y', color = 'r', fontsize = 14)

43

ax22.set_ylabel('z', color = 'g', fontsize = 14)

44

ax21.legend(loc = "upper left") # add legend including LaTeX commands

45

ax22.legend(loc = "lower right")

46

py.show() # show the figure

47

48

two_axes()

<figure><img src="image_55.png" /><figcaption>FIGURE A.3 – Doubler un axe avec `axes`.</figcaption></figure>

Une figure peut-être considérée comme une page blanche. Les **axes** permettent de positionner un repère sur la page de la figure à l'endroit désiré comme le montre le script suivant

1 import matplotlib.pyplot as plt

2 import numpy as np

3

4 fig = plt.figure(figsize=(10, 5))

5 ax = fig.add_subplot(111)

6

7 x = np.linspace(0, 2*np.pi, 100)

8 y = np.cos(x)

9

10 ax.plot(x, y, 'r-')

11

12 ax2 = fig.add_axes([0.3, 0.7, 0.4, 0.2])

13 ax2.plot(x, np.sin(x), 'g-')

14 ax2.set_title("I can just do anything with axes!")

15

16 plt savefig("../figures/pm_A3_1.pdf", bbox_inches = 'tight')

17 plt.show()

Ces deux programmes répondent à la question : Quelle est l’intérêt de passer par la définition d’un **axes**. Aucun si vous ne faites que des graphiques simples ; obligatoire dès que vous souhaiter prendre le contrôle de votre figure et l’adapter réellement à vos besoins.

Enfin quand vous utilisez des `subplot()` comme

1 fig=figure()

2 ax1=fig.add_subplot(211)

3 ax2=fig.add_subplot(212)

vous situez entre deux eaux. `matplotlib` vous aide de cette façon à poser vos repères sur une grille ordonnée.
<figure><img src="image_56.png" /><figcaption>FIGURE A.4 – Positionnement des repères avec axes.</figcaption></figure>

## A.5 Collections et formes géométriques

Un exemple parmi d'autres mais important pour nous par la suite : l'utilisation de formes géométriques. Ces formes sont définies dans des classes, la classe Circle que nous allons utiliser par exemple, et font partie d'un ensemble appelé les patches. chaque forme peut être utilisée indépendamment ou regroupée dans des ensembles de formes que l'on appelle des collectons de patches (PatchCollection). L'intérêt de regrouper les formes réside dans la possibilité offerte de leur attribuer des propriétés communes (même couleur etc...).

Nous allons dessiner des cercles (figure A.5)...

```python
1 # -*- coding: utf-8 -*-
2 """
3 pm_A4.py
4 
5 Drawing Circle
6 """
7 
8 
9 import matplotlib.pyplot as plt
10 import matplotlib.patches as pch
11 import matplotlib.collections as col
12 from matplotlib.lines import Line2D
13 
14 def first():
15     fig = plt.figure()
16     ax = fig.add_subplot(111)
17 
18     # Create the circle
19     c = plt Circle([25, 25], radius = 10, fc = 'r', ec = 'k', alpha = 0.5)
20     #add it to the figure
21     ax.addArtist(c)
22 
23     # Set axis form
24     ax.axis('square')
25     # Fix the limits. Axes will not know the limits from the Circle instance
26     ax.set_ylim(0, 50)
27     ax.set_xlim(0, 50)
28 
29     # Tricky Legend. Add a handle because legend can not get labels from a Circle (or
30     # a Rectangle or a Polygon)
31     hd = [Line2D([0], [0], marker = 'o', markersize = 14, markerfacecolor = 'r',
32                  markeredgecolor = 'k', lw = 0, alpha = 0.5)]
31     ax.legend(hd, ['A ⊞ red ⊞ circle'])
32 
33     # Save and show
34     plt savefig("../figures/Circle.pdf", bbox_inches = 'tight')
35     plt.show()
```
36

37 def second():

38 fig = plt.figure()

39 ax = fig.add_subplot(111)

40

41 # Create the red circles

42 circles = []

43 for i in range(10):

44 c = pch Circle([4*i,2*i], radius = 0.5*(i+1))

45 circles.append(c) # add the circle to a list of circles

46 # Create a collection of circles

47 ccol = col.PatchCollection(circles, edgecolor='k', facecolor='r', alpha = 0.5, label='red')

48 # Add them to the figure

49 ax.add_collection(ccol)

50

51 # s=Same for the green circles

52 circles = []

53 for i in range(10):

54 c = pch Circle([2*i,4*i], radius = (i+1))

55 circles.append(c)

56 ccol = col.PatchCollection(circles, edgecolor='k', facecolor='g', alpha = 0.5, label='green')

57 ax.add_collection(ccol)

58

59 # Set the axis.

60 ax.axis('square')

61 ax.set_ylim(0,50)

62 ax.set_xlim(0,50)

63

64 # Tricky legend

65 # Create the handles using a Line2D object

66 hd = [Line2D([0], [0], marker='o', markersize=14, markerfacecolor='r', markeredgedecolor='k', lw=0, alpha=0.5), Line2D([0],[0], marker='o', markersize=14, markerfacecolor='g', markeredgedecolor='k', lw=0, alpha=0.5)]

67 # Create the legend calling the handles and adding labels.

68 ax.legend(hd, ['Red', 'circles', 'Green', 'circles'], labelspacing = 1.2)

69

70 # Save and show

71 plt.savefig '../figures/Circles.pdf', bbox_inches = 'tight')

72 plt.show()

73

74 second()

La différence entre les deux fonctions vient de l'usage de ce que l'on nomme des collections. Celles-ci permettent de regrouper dans un même ensemble des formes aux propriétés équivalentes. Nous nous en servirons lorsqu'il s'agira de colorier des polygones selon différents critères.

## A.6 Légendes

Comme pour tout en python, il y a plusieurs façons de représenter les légendes de figures, de la plus simple à la plus complexe. Voici quelques exemples. Comme toujours les arguments de la commande `legend` sont nombreux, et l'aide en ligne de matplotlib fort utile! Vous retrouverez dans la partie cartographie d'autres exemples montrant notamment l'utilisation de `colormap`.

1 # -*- coding: utf-8 -*-

2 """

3 pm_A5.py

4

5 Different ways to build a legend

6 """

7 #import needed libraries

8 import matplotlib.pylab as py

9 from matplotlib.lines import Line2D

10 import numpy as np
<figure><img src="image_57.png" /><figcaption>FIGURE A.5 – Utilisation des objets collections et patches</figcaption></figure>

```python
def leg_1():
    """Adding labels"""
    # Define the data
    x = py.linspace(0, 10, 100)
    y = x * py.cos(x)
    z = x * py.sin(x)

    py.figure() #define the figure
    py.plot(x, y, 'r-', label = '$x\cos(x)$') #LABELS PLACED INSIDE THE PLOT
    py plot(x, z, 'g-', label = '$x\sin(x)$')

    py.xlabel('x', fontsize = 14) #add labels
    py.ylabel('y', fontsize = 14)

    #LEGEND CALL
    py.legend(loc = "upper left") # add legend including LaTeX commands

    py.show() # show the figure
```

```python
def leg_2():
    """Adding labels second version"""
    # Define the data
    x = py.linspace(0, 10, 100)
    y = x*py.cos(x)
    z = x*py.sin(x)

    py.figure() # Define the figure
    py.plot(x, y, 'r-')
    py plot(x, z, 'g-')

    py.xlabel('x', fontsize = 14) # Add labels
    py.ylabel('y', fontsize = 14)

    # LEGEND CALL
```
50

```python
py.legend labels = ['$x\cos(x)$', '$x\sin(x)$'], loc = "lower left") # add
legend including LaTeX commands
```

51

```python
py.show() # Show the figure
```

52

53

```python
def leg_3():
```

54

```python
"""Retrieving axes, handles and labels"""
```

55

56

```python
# Define the data
x = py.linspace(0, 10, 100)
y = x * py.cos(x)
z = x * py.sin(x)
```

57

58

59

60

61

```python
fig = py.figure() #define the figure
```

62

```python
ax = fig.add_subplot(111)
```

63

```python
ax.plot(x, y, 'r-', label = '$x\cos(x)$')
```

64

```python
ax.plot(x, z, 'g-', label = '$x\sin(x)$')
```

65

66

```python
py.xlabel('x', fontsize = 14) #add labels
```

67

```python
py.ylabel('y', fontsize = 14)
```

68

69

```python
# LEGEND CALL
```

70

```python
handles, labels = ax.get legend_handles labels ()
```

71

```python
py.legend(handle s, labels, bbox_to anchor = (0.5, 1.1), ncol = 2, loc = 'center') # add legend including LaTeX commands
```

72

73

74

```python
py.show() # show the figure
```

75

76

77

```python
def leg_4():
```

78

```python
"""Phantom legend: very useful to represent collections and group labels"""
```

79

80

```python
fig = py.figure() #define the figure
```

81

```python
ax = fig.add_subplot(111)
```

82

83

84

```python
# Create a phantom plot
for i in range(4):
```

85

```python
    py.scatter( [], [], s = np. exp(i + 1), edgecolor = 'k', facecolor = 'none', label = str(i + 1))
```

86

87

88

```python
# Get the legend handles and labels
h, l = py.gca().get legend_handles labels ()
```

89

90

91

```python
# Add a new handle and label
h.append(Line2D([0], [0], color = 'r', lw = 3))
```

92

93

```python
l.append('a red line!')
```

94

```python
ax.legend(h, l, title = "Phantoms", labelspacing = 2, borderpad = 2, frameon
         = True, ncol = 5, loc = 2)
```

95

96

```python
py.show()
```

97

98

## A.7 La fonction zip

cette fonction est importante car très utile lorsqu'on fait de la cartographie et qu'on veut associer ou dissocier des listes/tuples de données à des fins d'itération. Attention zip fonctionne différemment en python 2 et 3

1

```python
# -*- coding: utf-8 -*-
```

2

3

```python
print("original tuples")
```

4

```python
x1 = (0,1,2)
```

5

```python
y1 = (1,2,3)
```

6

```python
print(x1)
```

7

```python
print(y1)
```

8
```python
9 print("zip function")
10 xy_iter = zip(x1, y1)
11 xy = list(xy_iter)
12 print(xy)
13
14 print("and back")
15 x2, y2 = zip(*xy)
16 print(x2)
17 print(y2)
18
19 print(*x2)
```
# Annexe B

## Bases de données supplémentaires

Ce chapitre permet de s'entraîner sur d'autres bases de données, environnementales ou non. Il vous permettra d'affermir votre maîtrise des requêtes.

### B.1 La base GEMS

Nous allons travailler sur une base de données appelée GEMS (Global Environment Monitoring System) du nom du programme dont elle est issue. Contrairement au caractère général de son nom, cette base porte en fait sur les caractéristiques d'un échantillon des plus grands fleuves du monde. Elle comporte 6 tables dont les sujets sont

<table><thead><tr><td>Nom de la table</td><td>Sujet</td></tr></thead><tbody><tr><td>TblContinent</td><td>Continent auquel appartient un fleuve</td></tr><tr><td>TblExutoire</td><td>Exutoire vers lequel draîne un fleuve</td></tr><tr><td>TblGemsInfoBassin</td><td>Informations géographiques sur les fleuves</td></tr><tr><td>TblFlux</td><td>Données physiques et flux</td></tr><tr><td>TblDissout</td><td>Chimie des éléments majeurs en solution</td></tr></tbody></table>

La base étant ici à vocation pédagogique les informations sont dans certains cas rudimentaires. Le découpage en tables différentes permet de différencier les sujets de chaque table. En effet je rappelle (encore !) que dans une base de données relationnelles une table se rapporte à un sujet bien défini. Le choix des sujets doit permettre de modifier les données d'une table (hormis certains champs particuliers) sans affecter les données des autres tables voire les autres champs d'une même table. On pourrait ainsi ajouter une description détaillée de chaque site d'étude dans la table TblSiteEch, ou des informations complémentaires sur les continents dans la tables TblContinent sans avoir ni à modifier la structure de la base et encore moins à toucher les autres tables. Les clés primaires de la base GEMS sont :

<table><thead><tr><td>Table</td><td>clé primaire</td></tr></thead><tbody><tr><td>TblContinent</td><td>CodeContinent</td></tr><tr><td>TblExutoire</td><td>CodeExut</td></tr><tr><td>TblGemsInfoBassin</td><td>Bassin<sup>1</sup></td></tr><tr><td>TblFlux</td><td>CodeFlux</td></tr><tr><td>TblDissout</td><td>CodeDissout</td></tr></tbody></table>

Une fois ces champs définis il est alors simple de relier les tables entre elles. En effet tout enregistement de données de flux de la table TblFlux se rapportent à un bassin de la table TblGemsInfoBassin. Pour créer physiquement ce lien il suffit d'ajouter comme champs dans un enregistrement de la table TblFlux le champs Bassin de la table TblGemsInfoBassin. Ce champs n'a pas besoin d'être une clé (on peut avoir plusieurs enregistrements de flux pour une même rivière. Par contre il doit être identique (format et contenu) au champs de la table qu'il référence. Un tel champs est appelé une clé étrangère (Foreign Key) au sens où il fait référence à un table dans laquelle il a le statut de Clé primaire.

La figure B.1 montre la façon dont les tables TblGemsInfoBassin et TblExutoire sont liées par un champs commun, qui a le statut de clé primaire dans le cas de la table exutoire et le statut de clé étrangère dans la table TblGemsInfoBassin. On notera que ce champs n'a pas le même nom. Peu importe en effet le nom seule la définition du champs et son contenu comptent. De plus un même code d'exutoire arrive fréquemment dans la table TblGemsInfoBassin. En effet plusieurs fleuves ont leur exutoire en Atlantique Nord par exemple.

<footnote n="1">1. En toute rigueur prendre le nom d'un bassin versant comme clé primaire n'est pas un bon choix. Pourquoi ?</footnote>
<figure><img src="image_58.png" /><figcaption>FIGURE B.1 – Lien entre deux tables</figcaption></figure>

La structure de la base avec ses clés et liens est présentée dans la figure B.2. On notera que, dans le but de faciliter les requêtes que vous allez devoir exécuter, et vous éviter des requêtes demandant le recours à trois tables, un champs est redondant et ne devrait pas en toute logique figurer dans une des tables. Lequel et pouquoi ?

B.1.1 Requêtes simples sur une table

1. Combiens d'enregistrements dans la table TLGemsInfoBassins ?

2. Combiens d'enregistrements dans la table TblFlux ?

3. Combiens d'enregistrements dans la table ContinentTbl ?

4. Quel est le débit en m3/s des fleuves de la table TblFlux classé par ordre décroissant de débit ?

5. Quel est le débit solide annuel des fleuves de la table TblFlux classé par ordre décroissant ?

6. Quel est le débit, le débit solide et le débit dissout (en unités SI) de la Chao Phraya ?

7. Quelle est la concentration en cations du Huang He ?

8. Quelle est la concentration en anions de la Seine ?

9. Quelle est la concentration en cations divalents de la Loire ?

10. Quel est le rapport entre concentration solide et concentration dissoute des fleuves de la base ?

11. Combien de ces fleuves ont un ratio supérieur à 1 ?

12. Combien de ces fleuves n'ont pas de ratio définit ?

B.1.2 Requêtes sur une ou plusieurs tables

1. Liste des bassins par continents ?

2. Liste des bassins d'Europe et d'Asie

3. Combien de bassins par continent ?

4. Liste des bassins et de leur Exutoire ordronnée par Exutoire ?

5. Combien de bassins par type d'exutoire ?

6. Combien de bassins en Amérique du sud ?

7. Quel pourcentage de la surperficie du territoire couvrent les fleuves français de la base ?

8. Quel est le débit total annuel de chaque continent

9. Quelle est la charge sédimentaire annuelle (solide et dissoute) pour chaque continent en Mégatonnes par an ? Ordonnez le résultat par valeur décroissante de la charge

10. Quel est le flux de matière solide exporté en France en tonnes par an ?

11. Quel est le rapport flux solide, flux dissout pour chacune des rivières françaises

12. Quel est le rapport de ces flux pour chaque continent ? Ordonnez le résultat pra rapport décroissant.

13. Combien de rivières dans chaque continent ont un débit liquide supérieur à 10000 m3/s ?

14. Quel est le débit du plus gros Fleuve d'Europe ?

B.1.3 Les requêtes emboîtées

1. Quel est le débit du plus gros fleuve d'Europe et quel est ce fleuve ? Deux solutions. Pour des raisons de précisions il faut éviter de faire les calculs dans la sélection du débit max. on peut le faire avec le débit annuel d'origine donné en valeurs entières sans problème.

2. Quelle est la contribution de l'Amazone au flux liquide, solide et dissout arrivant chaque année dans l'océan Atlantique ?
<figure><img src="image_59.png" /><figcaption>FIGURE B.2 – Schéma de la Base GEMS</figcaption></figure>
## B.2 Les Charts

La musique sauvera le monde. Un peu de détente donc... La base de données **Charts** ne contient qu'une table **SingleFrance** (table B.1) qui recense tous les singles du top 50 depuis sa création et jusque vers la fin 2018 (je la mets à jour quand j'ai le temps).

<table><caption>TABLE B.1 – Structure de SingleFrance</caption><thead><tr><td>champs</td><td>type</td><td>description</td></tr></thead><tbody><tr><td>year</td><td>int</td><td>année</td></tr><tr><td>week</td><td>int</td><td>semaine de l'année</td></tr><tr><td>rank</td><td>int</td><td>rang entre 1 et 50</td></tr><tr><td>artist</td><td>varchar(255)</td><td>nom de l'artiste ou du groupe</td></tr><tr><td>song</td><td>varchar(255)</td><td>nom de la chanson</td></tr></tbody></table>

### B.2.1 Requêtes simples

1. Quelle est la liste des artistes et de leurs chansons présents dans la base classée par nom d'artiste décroissant et nom de chanson croissant.

2. Quelle est la liste des artistes qui ont été premiers au top 50

3. Comment se nomment les artistes dont le nom commence par «a»

4. Quels sont les artistes dont le nom contient «john»

5. Quelle est la liste des artistes ayant occupé la 1ère, la seconde ou la troisième position durant l'année 1991 ?

6. Quelle est la liste des chansons comprenant le mot "love" ou le mot "hate" dans leur titre ?

7. Quelle est la liste des chansons premières au top 50 entre 1985 et 1987 puis entre 1995 et 1997 ?

8. Quelle est la liste des artistes qui ont été premiers ou derniers en 1980, 1990, 2000 et 2010 ?

9. Combien d'enregistrements (de lignes) contient la table SingleFrance ?

10. Combien d'enregistrements (de lignes) contient la table SingleFrance pour chaque année du top50 ?

11. Combien d'artistes sont référencés dans la base ?

12. Combien de semaines chaque artiste a-t-il été présent dans le top 50 ?

13. Pour chaque chanson, combien de semaines un artiste a-t-il été dans le top 50 ?

14. Combien de chanson, dont le titre contient "love", ont-elle été premières au top 50

15. Combien d'artistes ont été derniers entre 2000 et 2010 ?

16. Pour un artiste de votre choix, et pour une de ses chansons, quelle est la trajectoire de celle-ci dans le top 50 ?

17. Toujours pour un artiste de votre choix, combien de semaines a-t-il (ou elle) occupé chacune de ses différentes positions au top 50 ?

18. Quels sont les artistes qui ont été plus de 100 semaines dans le top ?

19. Quels sont les artistes qui ont été plus de 10 semaines au premier rang ?

20. Quels sont les artistes qui ont été le plus au dernier rang ?

### B.2.2 Requêtes emboîtées

1. Combien d'années chaque artiste a-t-il été présent au top 50 ?

2. Pour un artiste de votre choix, sur l'ensemble de sa production combien de semaines en moyenne occupa-t-il à une position quelconque ?

3. Même question pour tous les artistes ayant eu au moins 10 chansons au top.

4. Quelle est le temps moyen qu'un artiste passe à chacune des 50 positions du classement sachant son rang ?
# Annexe C

## Ressources internet

Liens valides le 16/09/2018.

### C.1 L

'aide en ligne est gigantesque. Je vous donne un site qui vous permet d'obtenir les informations fondamentales. Et sinon entrez votre question sur n'importe que moteur de recherche et si vous ne trouvez pas la réponse c'est que soit vous ne savez pas vous y prendre, soit votre question commence a être réellement difficile, soit votre question est idiote (il faut toujours considérer ce point comme possible :=)).

1. Un site sur le SQL avec des cours, des liens etc... toujours en français ! https://sql.sh/

### C.2 Python

Idem l'aide en ligne est gigantesque. Entrez votre question dans un moteur de recherche et vous obtiendrez probablement une réponse. N'oubliez juste pas les quatre liens importants pour numpy, scipy, matplotlib et pandas

1. http://www.numpy.org/

2. https://scipy.org/

3. https:// matplotlib.org/

4. https://pandas.pydata.org/

### C.3 Basemap, cartopy et SIG

Là encore de nombreux sites mais qui reprennent presque tous les même bases. En voici quelques uns que je trouve utiles.

1. Le tutoriel en ligne, très bien fait : https://basemaptutorial.readthedocs.io/en/latest/

2. Idem le site de cartopy est très bien fait et accessible https://scitools.org.uk/cartopy/docs/latest/

3. Quelques exemples de raster https://rabernat.github.io/research_computing/intro-to-basemap.html

4. Le cookbook ogr/gdal mais il n'est pas très friendly d'accès https://pcjericks.github.io/py-gdalogr-cookbook/

5. Un excellent cours sur les données vectorielles et raster et l'utilisation d'ogr et de gdal. Aride et pas de cartographie à proprement parler mais vraiment bien https://www.gis.usu.edu/chrisg/python/2009/

6. Des ressources intéressantes mais il faut chercher un peu. En français : http://www.portailsig.org/

7. Enfin une liste de librairies spatiales en python https://github.com/SpatialPython/spatial_python