You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Plotly地图实现悬停/点击弹出视频URL及点位不显示问题排查

问题解决:Plotly地图点位消失+悬停/点击弹出视频

问题背景

用Plotly编写的地图点位脚本,原本可正常显示点位,鼠标悬停能展示pandas DataFrame列信息,但将悬停展示的列替换为包含视频URL的列后,地图点位完全消失。需要解决该问题,并实现鼠标悬停/点击时弹出视频(来自URL)的功能。

问题原因

  1. 视频URL含&、=等特殊字符,直接作为悬停文本会导致Plotly模板解析失败,破坏点位渲染逻辑
  2. 未配置Plotly的自定义悬停模板,默认文本处理规则无法兼容URL格式内容
  3. 原代码未显式定义悬停相关参数,替换列时未适配Plotly的HTML渲染规则

解决方案

核心修改点

  1. 使用自定义悬停模板(hovertemplate),嵌入HTML <video> 标签实现悬停框内直接播放视频
  2. 通过customdata传递视频URL,避免特殊字符干扰Plotly的文本解析
  3. 可选添加点击事件逻辑,实现点击点位弹出独立视频窗口

完整修改代码

import plotly.graph_objs as go
import plotly
import pandas as pd
from plotly.offline import plot

# 设置Mapbox令牌
mapbox_access_token = 'map_token_here'
df = pd.read_csv('cvs_with-data')
video_type = list(df['Type'].value_counts().index)

data = []
for video in video_type:
    # 筛选对应类型的数据
    filtered_df = df.loc[df['Type'] == video]
    video_data = dict(
        lat=filtered_df['Latitude'],
        lon=filtered_df['Longitude'],
        name=video,
        marker=dict(size=14),
        type='scattermapbox',
        # 传递视频URL作为自定义数据(替换成你的视频URL列名)
        customdata=filtered_df['Video_URL'],
        # 自定义悬停模板:嵌入HTML视频标签
        hovertemplate='<b>类型:</b> %{fullData.name}<br>' +
                      '<b>视频:</b><br><video width="200" controls src="%{customdata}"></video><extra></extra>'
    )
    data.append(video_data)

layout = dict(
    height=800,
    margin=dict(t=0, b=0, l=0, r=0),
    font=dict(color='#FFFFFF', size=11),
    paper_bgcolor='#000000',
    mapbox=dict(
        accesstoken=mapbox_access_token,
        bearing=0,
        center=dict(
            lat=57,
            lon=-4
        ),
        pitch=0,
        zoom=5,
        style='dark'
    ),
)

updatemenus = list([
    # 地图样式下拉菜单
    dict(
        buttons=list([
            dict(args=['mapbox.style', 'dark'], label='Dark', method='relayout'),
            dict(args=['mapbox.style', 'light'], label='Light', method='relayout'),
            dict(args=['mapbox.style', 'outdoors'], label='Outdoors', method='relayout'),
            dict(args=['mapbox.style', 'satellite-streets'], label='Satellite with Streets', method='relayout')
        ]),
        direction='up',
        x=0.75,
        xanchor='left',
        y=0.05,
        yanchor='bottom',
        bgcolor='#000000',
        bordercolor='#FFFFFF',
        font=dict(size=11)
    ),
    # 视频类型筛选下拉菜单(动态适配类型数量)
    dict(
        buttons=list([
            dict(label='All Testimonies', method='update', args=[{'visible': [True]*len(video_type)}]),
            dict(label='Stakeholder', method='update', args=[{'visible': [i==0 for i in range(len(video_type))]}]),
            dict(label='Scientist', method='update', args=[{'visible': [i==1 for i in range(len(video_type))]}]),
            dict(label='Member of the public', method='update', args=[{'visible': [i==2 for i in range(len(video_type))]}])
        ]),
        direction='down',
        x=0.01,
        xanchor='left',
        y=0.99,
        yanchor='bottom',
        bgcolor='#000000',
        bordercolor='#FFFFFF',
        font=dict(size=11)
    )
])

layout['updatemenus'] = updatemenus

annotations = [dict(
    text='Video Testimonials across Scotland',
    font=dict(color='#FFFFFF', size=14),
    borderpad=10,
    x=0.05,
    y=0.05,
    xref='paper',
    yref='paper',
    align='left',
    showarrow=False,
    bgcolor='black'
)]

layout['title'] = 'Video testimonials'
layout['annotations'] = annotations

# 点击弹出视频的自定义JS(可选功能)
custom_js = """
<script>
document.addEventListener('DOMContentLoaded', function() {
    var plotDiv = document.getElementById('myPlot');
    plotDiv.on('plotly_click', function(data) {
        var videoUrl = data.points[0].customdata;
        var modal = document.createElement('div');
        modal.style.position = 'fixed';
        modal.style.top = '50%';
        modal.style.left = '50%';
        modal.style.transform = 'translate(-50%, -50%)';
        modal.style.zIndex = '1000';
        modal.style.backgroundColor = 'black';
        modal.style.padding = '10px';
        var video = document.createElement('video');
        video.width = '600';
        video.controls = true;
        video.src = videoUrl;
        modal.appendChild(video);
        var closeBtn = document.createElement('button');
        closeBtn.textContent = 'Close';
        closeBtn.style.color = 'white';
        closeBtn.style.backgroundColor = '#333';
        closeBtn.style.marginTop = '10px';
        closeBtn.onclick = function() {
            document.body.removeChild(modal);
        };
        modal.appendChild(closeBtn);
        document.body.appendChild(modal);
    });
});
</script>
"""

# 生成HTML文件,包含自定义JS逻辑
figure = dict(data=data, layout=layout)
plot(figure, filename='Test.html', auto_open=False, include_plotlyjs='cdn', post_script=custom_js, div_id='myPlot')

关键说明

  1. 悬停视频实现:通过hovertemplate嵌入HTML视频标签,%{customdata}会自动替换为对应点位的视频URL,实现悬停时直接播放
  2. 点位消失修复:用customdata传递URL,避免特殊字符破坏Plotly的文本解析流程,确保点位正常渲染
  3. 点击弹窗功能:自定义JS监听plotly_click事件,点击点位后生成独立弹窗播放视频,优化交互体验
  4. 动态适配:类型筛选按钮的visible参数通过len(video_type)动态生成,避免硬编码数量导致的错误

内容的提问来源于stack exchange,提问作者Konstantinos Sideris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 13:46:04