Analyser un fichier GPX
Cela consiste à analyser un fichier GPX d'un itinéraire pédestre, pour faire la part de ce qui est "motorisé" et de ce qui ne l'est pas, en fonction de la nature de la trace.
Voici le code qui fait ça, à l'aide d'un serveur Flask,
from flask import Flask, request, render_template, send_file
import gpxpy
import requests
import folium
from geopy.distance import geodesic
import os
app = Flask(__name__)
def extract_coordinates_from_gpx(gpx_file):
coordinates = []
with open(gpx_file, 'r') as file:
gpx = gpxpy.parse(file)
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
coordinates.append((point.latitude, point.longitude))
return coordinates
def sample_points(coordinates, sample_rate=5):
return coordinates[::sample_rate]
def get_road_nature(coordinate):
overpass_url = "http://overpass-api.de/api/interpreter"
overpass_query = f"""
[out:json];
(way(around:1,{coordinate[0]},{coordinate[1]}););
out body;
"""
response = requests.get(overpass_url, params={'data': overpass_query})
if response.status_code != 200:
return None
data = response.json()
motorized_highways = ['motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'residential', 'unclassified']
for element in data.get('elements', []):
if 'tags' in element and 'highway' in element['tags']:
highway_type = element['tags']['highway']
print(f"Highway type : {highway_type} ")
if highway_type in motorized_highways:
return "motorized"
return "foot"
def distance_between_points(p1, p2):
return geodesic(p1, p2).meters
def split_into_homogeneous_segments(coordinates):
"""
Divise l'itinéraire en segments homogènes tout en calculant leur longueur.
:param coordinates: Liste des coordonnées [(lat, lon), ...]
:param min_distance: Distance minimale (en mètres) pour considérer un changement de nature
:return: Liste des segments homogènes sous forme de tuples (segment, longueur),
et les distances totales pour foot et motorized
"""
segments = [] # Liste des segments, chaque segment est un tuple (points, longueur)
current_segment = [coordinates[0]] # Le premier point commence un segment
current_length = 0 # Longueur cumulée du segment actuel
current_nature = get_road_nature(coordinates[0]) # Nature de la route pour le premier point
total_foot = 0
total_motorized = 0
for i in range(1, len(coordinates)):
nature = get_road_nature(coordinates[i])
distance = distance_between_points(coordinates[i-1], coordinates[i])
if nature == current_nature:
current_segment.append(coordinates[i])
current_length += distance # Ajouter la distance au segment actuel
else:
# Ajouter le segment terminé avec sa longueur
segments.append((current_segment, current_length))
if current_nature == "foot":
total_foot += current_length
else:
total_motorized += current_length
# Commencer un nouveau segment
current_segment = [coordinates[i]]
current_length = distance # Réinitialiser la longueur pour le nouveau segment
current_nature = nature
# Ajouter le dernier segment
if current_segment:
segments.append((current_segment, current_length))
if current_nature == "foot":
total_foot += current_length
else:
total_motorized += current_length
return segments, total_foot, total_motorized
def plot_on_map(gpx_file, sample_rate=5):
"""
Visualise la trace GPX sur une carte avec des waypoints à chaque changement de section homogène.
Affiche également la longueur de chaque segment homogène.
"""
coordinates = extract_coordinates_from_gpx(gpx_file)
sampled_coordinates = sample_points(coordinates, sample_rate)
homogeneous_segments, total_foot, total_motorized = split_into_homogeneous_segments(sampled_coordinates)
map_center = sampled_coordinates[0]
map_ = folium.Map(location=map_center, zoom_start=14)
# Ajouter la trace complète
folium.PolyLine(coordinates, color="blue", weight=2.5, opacity=1).add_to(map_)
# Ajouter des waypoints et afficher les longueurs des segments homogènes
for segment, length in homogeneous_segments:
start = segment[0]
end = segment[-1]
nature = get_road_nature(start)
folium.Marker(location=start, popup=f"Start: {nature}").add_to(map_)
folium.Marker(location=end, popup=f"End: {nature}, Length: {length:.2f}m").add_to(map_)
# Ajouter les totaux dans un coin de la carte
total_info = f"Total foot: {total_foot:.2f} meters<br>Total motorized: {total_motorized:.2f} meters"
folium.Marker(
location=map_center,
popup=total_info,
icon=folium.Icon(color="green", icon="info-sign")
).add_to(map_)
return map_
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
if 'file' not in request.files:
return "No file uploaded", 400
file = request.files['file']
if file.filename == '':
return "No selected file", 400
if not file.filename.endswith('.gpx'):
return "Invalid file type. Please upload a GPX file.", 400
file_path = os.path.join("uploads", file.filename)
file.save(file_path)
map_ = plot_on_map(file_path)
map_file = "templates/map.html"
map_.save(map_file)
return render_template("map.html")
return render_template("index.html")
if __name__ == "__main__":
os.makedirs("uploads", exist_ok=True)
app.run(debug=True)