Différences entre les versions de « Analyser un fichier GPX »

De Wiki de Mémoire Vive
Aller à la navigation Aller à la recherche
(Page créée avec « 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, <pre> 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):... »)
 
 
(2 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
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.
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.
Le script Flask (Python) permet à l'utilisateur de télécharger un fichier GPX, qui est ensuite traité pour extraire les coordonnées géographiques. Ces coordonnées sont segmentées en fonction de la nature des routes (pied ou motorisé) et les distances correspondantes sont calculées. Une carte interactive est générée à l'aide de Folium, affichant la trace GPX, des segments homogènes, et des marqueurs pour chaque segment avec des informations sur la nature de la route et la distance. Enfin, le fichier map.html contenant cette carte est créé dynamiquement et affiché à l'utilisateur via une page web.


Voici le code qui fait ça, à l'aide d'un serveur Flask,
Voici le code qui fait ça, à l'aide d'un serveur Flask,
Ligne 147 : Ligne 149 :
     os.makedirs("uploads", exist_ok=True)
     os.makedirs("uploads", exist_ok=True)
     app.run(debug=True)
     app.run(debug=True)
</pre>
Avec un fichier index.html dans un sous-dossier template,
<pre>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GPX Visualizer</title>
</head>
<body>
    <h1>GPX Trace Visualizer</h1>
    <form action="/" method="POST" enctype="multipart/form-data">
        <label for="file">Upload GPX File:</label>
        <input type="file" name="file" id="file" accept=".gpx">
        <button type="submit">Upload and Visualize</button>
    </form>
</body>
</html>


</pre>
</pre>

Version actuelle datée du 15 janvier 2025 à 18:34

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.

Le script Flask (Python) permet à l'utilisateur de télécharger un fichier GPX, qui est ensuite traité pour extraire les coordonnées géographiques. Ces coordonnées sont segmentées en fonction de la nature des routes (pied ou motorisé) et les distances correspondantes sont calculées. Une carte interactive est générée à l'aide de Folium, affichant la trace GPX, des segments homogènes, et des marqueurs pour chaque segment avec des informations sur la nature de la route et la distance. Enfin, le fichier map.html contenant cette carte est créé dynamiquement et affiché à l'utilisateur via une page web.

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)

Avec un fichier index.html dans un sous-dossier template,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GPX Visualizer</title>
</head>
<body>
    <h1>GPX Trace Visualizer</h1>
    <form action="/" method="POST" enctype="multipart/form-data">
        <label for="file">Upload GPX File:</label>
        <input type="file" name="file" id="file" accept=".gpx">
        <button type="submit">Upload and Visualize</button>
    </form>
</body>
</html>