如何基于TimestampedGeoJson实现带航向的船只路径动画?
实现带航向的船只路径航行动画
问题背景
已通过TimestampedGeoJson实现船只沿路径航行的基础动画:路径显示为绿色线条,船只标记为圆形。但圆形标记无法体现船只航向,需在保留原有路径显示、时间滑块、动画控制等功能的前提下,替换为带航向的船只标记。
基础实现代码如下:
# circle with following line features = [ { 'type': 'Feature', 'geometry': { 'type': 'LineString', 'coordinates': coordinates_list, }, 'properties': { 'popup': 'ship position', 'times': times_list, 'icon': 'circle', 'iconstyle': { 'fillColor': 'green', 'fillOpacity': 0.6, 'stroke': 'false', 'radius': 13, }, 'style': { 'color': 'green', 'weight': 2, 'opacity': 0.6 }, 'id': 'man', }, } ] plugins.TimestampedGeoJson( {'type': 'FeatureCollection', 'features': features}, period='PT30M', add_last_point=True, auto_play=False, loop=False, max_speed=1, loop_button=True, date_options='YYYY/MM/DD', time_slider_drag_update=True, duration='P1M', ).add_to(m)
解决方案
核心思路是:拆分路径与标记为两个独立Feature,计算每个路径点的航向角度,使用自定义可旋转图标作为船只标记。
步骤1:计算航向角度
通过两点坐标计算船只的航行方位角(航向),保证每个路径点对应正确的航向:
import math def calculate_bearing(lat1, lon1, lat2, lon2): # 计算两点间的方位角,返回0-360度的航向值 lon1, lat1, lon2, lat2 = map(math.radians, [lon1, lat1, lon2, lat2]) dlon = lon2 - lon1 x = math.cos(lat2) * math.sin(dlon) y = math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(dlon) bearing = math.degrees(math.atan2(x, y)) return (bearing + 360) % 360 # 为每个路径点计算航向(最后一个点复用前一个点的航向) bearings = [] for i in range(len(coordinates_list)): if i < len(coordinates_list) - 1: lon1, lat1 = coordinates_list[i] lon2, lat2 = coordinates_list[i+1] bearings.append(calculate_bearing(lat1, lon1, lat2, lon2)) else: bearings.append(bearings[-1] if bearings else 0)
步骤2:重构Feature集合
将原有的单个Feature拆分为静态路径线条和动态船只标记两个Feature,为船只标记配置带旋转的自定义图标:
# 静态路径线条Feature(无时间动画,仅显示路径) line_feature = { 'type': 'Feature', 'geometry': { 'type': 'LineString', 'coordinates': coordinates_list, }, 'properties': { 'style': { 'color': 'green', 'weight': 2, 'opacity': 0.6 } } } # 动态船只标记Feature(每个点对应时间与航向) ship_feature = { 'type': 'Feature', 'geometry': { 'type': 'MultiPoint', 'coordinates': coordinates_list, }, 'properties': { 'popup': 'ship position', 'times': times_list, 'icon': 'div-icon', # 用SVG绘制三角形作为船只图标,每个点对应不同的旋转角度(航向) 'iconstyle': [ { 'html': f'<svg width="20" height="20" viewBox="0 0 20 20"><polygon points="10,2 18,18 2,18" fill="#2e8b57" opacity="0.8"/></svg>', 'iconSize': [20, 20], 'iconAnchor': [10, 10], # 以图标中心为旋转点 'rotation': bearing } for bearing in bearings ], 'id': 'ship' } } # 合并两个Feature features = [line_feature, ship_feature] # 初始化TimestampedGeoJson,保留原有动画控制参数 plugins.TimestampedGeoJson( {'type': 'FeatureCollection', 'features': features}, period='PT30M', add_last_point=True, auto_play=False, loop=False, max_speed=1, loop_button=True, date_options='YYYY/MM/DD', time_slider_drag_update=True, duration='P1M', ).add_to(m)
可选优化:使用自定义PNG图标
如果有现成的船只PNG图标,可替换SVG部分,只需修改iconstyle的配置:
'iconstyle': [ { 'iconUrl': 'ship-icon.png', # 替换为你的图标路径 'iconSize': [30, 30], 'iconAnchor': [15, 15], # 确保旋转点为图标中心 'rotation': bearing } for bearing in bearings ]
关键说明
- 拆分Feature后,路径线条保持静态显示,船只标记随时间滑块动态移动并根据航向旋转
iconAnchor必须设置为图标中心,否则旋转方向会出现偏移- 若使用PNG图标,需确保图标默认船头朝上,这样旋转角度才能正确对应航行方向
内容的提问来源于stack exchange,提问作者Frank Tap
相关产品推荐
相关产品推荐

