codex/geospatial/haversine_distance.cat
# 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
#
# Exécuter:
# catnip docs/codex/geospatial/haversine_distance.cat
#
math = import("math")
# Rayon moyen de la Terre (km)
R = 6371.0
# Haversine : distance entre deux points (lat, lon) en degrés
haversine = (p1, p2) => {
dlat = math.radians(p2[0] - p1[0])
dlon = math.radians(p2[1] - p1[1])
a = math.sin(dlat / 2) ** 2 + math.cos(math.radians(p1[0])) * math.cos(math.radians(p2[0])) * math.sin(dlon / 2) ** 2
2 * R * math.asin(math.sqrt(a))
}
# Coordonnées de villes (lat, lon)
paris = list(48.8566, 2.3522)
london = list(51.5074, -0.1278)
new_york = list(40.7128, -74.0060)
tokyo = list(35.6762, 139.6503)
sydney = list(-33.8688, 151.2093)
cape_town = list(-33.9249, 18.4241)
rio = list(-22.9068, -43.1729)
moscow = list(55.7558, 37.6173)
# Distance directe entre paires
print("⇒ Distances depuis Paris")
print(" → Londres :", round(haversine(paris, london), 1), "km")
print(" → New York :", round(haversine(paris, new_york), 1), "km")
print(" → Tokyo :", round(haversine(paris, tokyo), 1), "km")
print(" → Sydney :", round(haversine(paris, 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(haversine(paris, city), 1) }]
print("\n⇒ 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("\n⇒ Extrêmes")
print(" Plus proche :", names[distances.index(closest)], "(", closest, "km )")
print(" Plus loin :", names[distances.index(farthest)], "(", farthest, "km )")
# Variante curried : haversine comme générateur de fonctions distance
dist_from = (ref) => {
(point) => { round(haversine(ref, point), 1) }
}
print("\n⇒ 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("\n⇒ 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(haversine(a, 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("\n⇒ 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("\n⇒ 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")
total = 0
for i in range(len(etapes)) {
d = round(haversine(trajet[i], trajet[i + 1]), 1)
print(" ", etapes[i], ":", d, "km")
total = total + d
}
print(" Total :", round(total, 1), "km")