#!/usr/bin/env catnip
# Distance géodésique : formule de Haversine
# Distance orthodromique entre deux points GPS (latitude, longitude)
# sur la sphère terrestre moyenne (R = 6371 km)
#
# Ref: https://en.wikipedia.org/wiki/Haversine_formula
math = import('math')
# Rayon moyen de la Terre (km)
R = 6371.0
struct Location {
lat; lon;
# Distance géodésique vers un autre point (km)
distance_to(self, other) => {
dlat = math.radians(other.lat - self.lat)
dlon = math.radians(other.lon - self.lon)
a = math.sin(dlat / 2) ** 2 +
math.cos(math.radians(self.lat)) * math.cos(math.radians(other.lat)) * math.sin(dlon / 2) ** 2
2 * R * math.asin(math.sqrt(a))
}
}
# Coordonnées de villes
paris = Location(48.8566, 2.3522)
london = Location(51.5074, -0.1278)
new_york = Location(40.7128, -74.0060)
tokyo = Location(35.6762, 139.6503)
sydney = Location(-33.8688, 151.2093)
cape_town = Location(-33.9249, 18.4241)
rio = Location(-22.9068, -43.1729)
moscow = Location(55.7558, 37.6173)
# Distance directe entre paires
print("⇒ Distances depuis Paris")
print(" → Londres :", round(paris.distance_to(london), 1), "km")
print(" → New York :", round(paris.distance_to(new_york), 1), "km")
print(" → Tokyo :", round(paris.distance_to(tokyo), 1), "km")
print(" → Sydney :", round(paris.distance_to(sydney), 1), "km")
# Broadcasting : distances d'un point de référence vers toutes les villes
cities = list(london, new_york, tokyo, sydney, cape_town, rio, moscow)
names = list("Londres", "New York", "Tokyo", "Sydney", "Le Cap", "Rio", "Moscou")
distances = cities.[(city) => { round(paris.distance_to(city), 1) }]
print()
print("⇒ Broadcasting : distances depuis Paris")
for i in range(len(names)) {
print(" ", names[i], ":", distances[i], "km")
}
# Plus proche et plus lointaine
closest = min(distances)
farthest = max(distances)
print()
print("⇒ Extrêmes")
print(" Plus proche :", names[distances.index(closest)], "(", closest, "km )")
print(" Plus loin :", names[distances.index(farthest)], "(", farthest, "km )")
# Variante curried : générateur de fonctions distance
dist_from = (ref) => {
(point) => { round(ref.distance_to(point), 1) }
}
print()
print("⇒ Variante curried : distances depuis Tokyo")
from_tokyo = dist_from(tokyo)
tokyo_distances = cities.[(city) => { from_tokyo(city) }]
for i in range(len(names)) {
print(" ", names[i], ":", tokyo_distances[i], "km")
}
# Matrice de distances
print()
print("⇒ Matrice de distances (km)")
sample = list(paris, london, new_york, tokyo, sydney)
sample_names = list("Paris", "Londres", "New York", "Tokyo", "Sydney")
matrix = sample.[(a) => { sample.[(b) => { round(a.distance_to(b)) }] }]
for i in range(len(sample_names)) {
row = sample_names[i]
for j in range(len(sample_names)) {
row = row + "\t" + str(matrix[i][j])
}
print(" ", row)
}
# Rayon de recherche : villes à moins de 5000 km de Paris
print()
print("⇒ Villes à moins de 5000 km de Paris")
for i in range(len(names)) {
if distances[i] < 5000 {
print(" ", names[i], ":", distances[i], "km")
}
}
# Périmètre d'un trajet
print()
print("⇒ Périmètre Paris → Londres → New York → Rio → Le Cap → Tokyo → Paris")
trajet = list(paris, london, new_york, rio, cape_town, tokyo, paris)
etapes = list("Paris→Londres", "Londres→NY", "NY→Rio", "Rio→Le Cap", "Le Cap→Tokyo", "Tokyo→Paris")
distances_etapes = range(len(etapes)).[(i) => { round(trajet[i].distance_to(trajet[i + 1]), 1) }]
for i in range(len(etapes)) {
print(" ", etapes[i], ":", distances_etapes[i], "km")
}
total = fold(distances_etapes, 0, (acc, d) => { acc + d })
print(" Total :", round(total, 1), "km")