Calcul de distances

Afin de pouvoir évaluer des distances, puis des vitesses à partir des coordonnées GPS contenues dans un fichier GPX, nous allons nous intéresser aux fonctions mathématiques, notamment trigonométriques, du module math.

Ce chapitre sera aussi l'occasion d'aborder la création de fonctions au sens informatique.

Le module math

Ce module est préinstallé avec Python, donc pas besoin d'opération particulière pour l'utiliser. Parmi les fonctions les plus courantes, on peut citer :

Fonction Description
floor(x) plus grand entier inférieur à x
ceil(x) plus petit entier supétieur à x
fabs(x) valeur absolue de x
sqrt(x) racine carrée de x
pow(x,y) x à la puissance y
degrees(x) conversion de l'angle x des radians en degrés
radians(x) conversion de l'angle x des degrés en radians
cos(x) cosinus de l'angle x exprimé en radians
sin(x) sinus de l'angle x exprimé en radians
tan(x) tangente de l'angle x exprimé en radians
acos(x) arc cosinus de x, le résultat est exprimé en radians
asin(x) arc sinus de x, le résultat est exprimé en radians
atan(x) arc tangente de x, le résultat est exprimé en radians
atan2(y,x) atan(y/x)
from math import *

# Calcul de l'hypoténuse d'un triangle rectangle
a = 3
b = 4
c = sqrt(a**2 + b**2)

Définition de fonctions

Nous avons vu que nous pouvions utiliser des modules en Python pour accéder à de nouvelles fonctions. Mais il est aussi possible de créer ses propres fonctions à l'aide de la clause def.

from math import *

# Définition de la fonction distance
def distance(x1, y1, z1, x2, y2, z2):
    dx = x2 - x1
    dy = y2 - y1
    dz = z2 - z1
    return sqrt( dx*dx + dy*dy + dz*dz )

# Utilisation de la fonction distance
d = distance(0, 0, 0.5, 1, 2, -4.5)

Les valeurs entre parenthèses, séparées par une virgule, qui suivent le nom de la fonction sont ses paramètres : ils sont utilisés comme des variables locales au sein de la fonction.

Exercice

Écrivez un script gpsutils.py au sein duquel vous définirez une fonction great_circle_distance(lat1, lon1, lat2, lon2) qui calcule la distance orthodromique entre deux points de coordoonées polaires (lat1,lon1) et (lat2,lon2) en utilisant la formule de Vincenty.

Attention !

Les latitudes et longitudes sont exprimées en degrés dans un fichier GPX alors que les fonctions trigonométriques de Python manipulent des angles en radians. La fonction radians() devrait vous dépanner.

Info

Pour plus de précision, vous utiliserez le rayon moyen de la Terre dans vos calculs.
Correction
from math import *

#
# Rayon de la Terre au niveau de l'équateur (en mètres)
# a = 6378137
#
# Rayon de la Terre au niveau des pôles (en mètres)
# b = 6356752 
#
# Rayon moyen de la Terre (en mètres)
# R = (2*a+b)/3 = 6371009
#
def great_circle_distance(lat1, lon1, lat2, lon2):
    lat1 = radians(lat1)
    lat2 = radians(lat2)
    dLon = radians(fabs(lon2-lon1))

    cosLat1 = cos(lat1)
    sinLat1 = sin(lat1)
    cosLat2 = cos(lat2)
    sinLat2 = sin(lat2)
    cosDLon = cos(dLon)
    sinDLon = sin(dLon)

    A = cosLat2*sinDLon
    B = cosLat1*sinLat2 - sinLat1*cosLat2*cosDLon

    return 6371009 * atan2(sqrt(A*A + B*B),
                     sinLat1*sinLat2 + cosLat1*cosLat2*cosDLon)

Pour utliser cette nouvelle fonction dans un autre script Python, nous pouvons l'importer comme un module :

from gpsutils import *

d = great_circle_distance(49.5, 4.9, 48, 7.12)

Le module geopy

Même si la formule de Vincenty est sophistiquée, elle repose sur l'hypothèse que la Terre est une sphère parfaite alors qu'en réalité, elle a la forme d'un ellipsoïde légèrement écrasé au niveau des pôles. Pour une estimation plus précise de la distance entre deux points sur la surface du globe, il convient d'utiliser la distance géodésique.

Si ce n'est pas déjà fait, vous devez installer ce module dans un terminal (invite de commandes sous Windows) en tapant la commande suivante :

Windows :

pip.exe install geopy

macOS ou Linux :

pip3 install geopy

Le module distance de geopy propose des fonctions pour calculer les distances orthodromiques et géodésiques entre deux points du globe à partir de leurs coordonnées polaires.

from geopy import distance as dst

paris = (48.864716, 2.349014)
lisbon = (38.736946, -9.142685)

print('PARIS - LISBONNE\n')
print('Distance orthodromique :', dst.great_circle(paris, lisbon).km, 'km')
print('Distance géodésique    :', dst.geodesic(paris, lisbon).km, 'km')
PARIS - LISBONNE

Distance orthodromique : 1452.2743880698056 km
Distance géodésique    : 1453.1714413569523 km

Info

La distance calculée par le module geopy peut être exprimée en km, m ou miles.

Exercice

Complétez le script gpxplorer.py en ajoutant une colonne distance au DataFrame qui contiendra, après parcours de l'arborescence XML, les distances cumulées le long du parcours.

Correction
from bs4 import BeautifulSoup as bs
import pandas as pd
from geopy import distance as dst

df = pd.DataFrame([], columns = [ 'datetime', 'latitude', 'longitude', 'elevation', 'distance' ])

# Chargement du fichier GPX
content = open('RATJ2012-21km-herve.schely.gpx')

# Construction de l'arborescnce
root = bs(content, features='xml')

# Remplissage du DataFrame
k = 0
for point in root.find_all('trkpt'):
    df.loc[k, 'datetime'] = point.find('time').string
    df.loc[k, 'latitude'] = float(point['lat'])
    df.loc[k, 'longitude'] = float(point['lon'])
    df.loc[k, 'elevation'] = float(point.find('ele').string)
    k = k + 1

# Calcul des distances cumulées
df.loc[0, 'distance'] = 0
for k in df.index[1:]:
    p1 = (df['latitude'][k-1], df['longitude'][k-1])
    p2 = (df['latitude'][k], df['longitude'][k])
    df.loc[k, 'distance'] = df.loc[k-1, 'distance']  + dst.geodesic(p1, p2).m

print('Distance parcourue :', round(df['distance'].iloc[-1]), 'm')

Félicitations, il semble que rien ne vous arrête ! Penchons-nous maintenant sur la gestion du temps afin d'enrichir notre script avec le calcul de la vitesse moyenne par exemple. On se donne rendez-vous au chapitre suivant dans quelques dixièmes de secondes...