diff --git a/hiking_assistant/app.py b/hiking_assistant/app.py index 5d23639..e07066a 100644 --- a/hiking_assistant/app.py +++ b/hiking_assistant/app.py @@ -73,6 +73,7 @@ class HikingAssistant: horizontal_speed=self._config['app']['estimated_time']['horizontal_speed'], vertical_speed=self._config['app']['estimated_time']['vertical_speed'], ) + waypoints = gpx_processor.waypoints # Cache all processed data st.session_state.gpx_file_key = file_key @@ -88,6 +89,7 @@ class HikingAssistant: st.session_state.elevations = elevations st.session_state.gradients = gradients st.session_state.estimated_time = estimated_time + st.session_state.waypoints = waypoints # Show altitude sickness warning for first gpx loading if max_elevation >= self._config['app']['altitude_sickness']['elevation_threshold']: @@ -106,6 +108,7 @@ class HikingAssistant: elevations = st.session_state.elevations gradients = st.session_state.gradients estimated_time = st.session_state.estimated_time + waypoints = st.session_state.waypoints # Display statistics section st.header('📊 路線五四三') @@ -136,7 +139,7 @@ class HikingAssistant: # Map section with st.spinner('正在渲染地圖...'): map_renderer = MapRenderer() - route_map = map_renderer.create_route_map(all_points, start_point, end_point) + route_map = map_renderer.create_route_map(all_points, start_point, end_point, waypoints) if route_map: st_folium(route_map, width=None, height=500, key='route_map', returned_objects=[]) @@ -144,7 +147,7 @@ class HikingAssistant: st.error('無法渲染地圖') # Create two columns: elevation profile on left, pie chart on right - profile_col, pie_col = st.columns([2, 1]) + profile_col, pie_col = st.columns([3, 1]) with profile_col: with st.spinner('正在繪製海拔剖面圖...'): diff --git a/hiking_assistant/gpx.py b/hiking_assistant/gpx.py index 4cddf5e..9e44a7e 100644 --- a/hiking_assistant/gpx.py +++ b/hiking_assistant/gpx.py @@ -23,6 +23,7 @@ class GPXProcessor: self.points = [] self.elevations = [] # unit: m self.distances = [] # unit: km + self.waypoints = [] # list of (lat, lon, name, elevation) def _get_sample_rate(self): """Get the median time interval between points. @@ -52,6 +53,17 @@ class GPXProcessor: self.gpx = gpxpy.parse(gpx_data) + # Extract waypoints + for waypoint in self.gpx.waypoints: + self.waypoints.append( + { + 'lat': round(waypoint.latitude, 6), + 'lon': round(waypoint.longitude, 6), + 'name': waypoint.name if waypoint.name else '航點', + 'elevation': round(waypoint.elevation, 1) if waypoint.elevation else None, + } + ) + # Extract points from all tracks and segments for track in self.gpx.tracks: for segment in track.segments: @@ -127,15 +139,15 @@ class GPXProcessor: return min(self.elevations), max(self.elevations) def get_start_end_points(self): - """Get start and end point coordinates. + """Get start and end point coordinates with elevation. Returns: - tuple: ((start_lat, start_lon), (end_lat, end_lon)) + tuple: ((start_lat, start_lon, start_elevation), (end_lat, end_lon, end_elevation)) """ if len(self.points) < 2: return None, None - return self.points[0], self.points[-1] + return (self.points[0][0], self.points[0][1], self.elevations[0]), (self.points[-1][0], self.points[-1][1], self.elevations[-1]) def get_all_points(self): """Get all route points. diff --git a/hiking_assistant/map.py b/hiking_assistant/map.py index 15e9b90..21d1747 100644 --- a/hiking_assistant/map.py +++ b/hiking_assistant/map.py @@ -13,13 +13,14 @@ class MapRenderer: """Initialize map renderer.""" pass - def create_route_map(self, points, start_point, end_point, tile_layer='OpenStreetMap'): + def create_route_map(self, points, start_point, end_point, waypoints=None, tile_layer='OpenStreetMap'): """Create an interactive map with the hiking route. Args: points: List of (latitude, longitude) tuples for the route start_point: (latitude, longitude) tuple for start end_point: (latitude, longitude) tuple for end + waypoints: List of waypoint dictionaries with 'lat', 'lon', 'name', 'elevation' tile_layer: Map tile layer to use ('OpenStreetMap' or 'RudyMap') Returns: @@ -71,29 +72,43 @@ class MapRenderer: tooltip='登山路線', ).add_to(route_map) + # Add waypoints if provided + if waypoints: + for waypoint in waypoints: + popup_text = f'{waypoint["name"]}' + if waypoint.get('elevation'): + popup_text += f'
海拔: {waypoint["elevation"]} m' + + folium.Marker( + location=[waypoint['lat'], waypoint['lon']], + popup=folium.Popup(popup_text, max_width=200), + tooltip=waypoint['name'], + icon=folium.Icon(color='orange', icon='info-sign'), + ).add_to(route_map) + # Add start point marker (blue circle) if start_point: folium.CircleMarker( - location=start_point, + location=(start_point[0], start_point[1]), radius=8, color='blue', fill=True, fill_color='blue', fill_opacity=0.7, - popup=start_point, + popup=f'起點
海拔: {start_point[2]} m', tooltip='起點', ).add_to(route_map) # Add end point marker (blue circle) if end_point: folium.CircleMarker( - location=end_point, + location=(end_point[0], end_point[1]), radius=8, color='blue', fill=True, fill_color='blue', fill_opacity=0.7, - popup=end_point, + popup=f'終點
海拔: {end_point[2]} m', tooltip='終點', ).add_to(route_map)