介绍

在日益数据驱动的世界中,可视化复杂空间信息的能力比以往任何时候都更加重要。无论您是优化配送路线的物流经理、研究交通模式的城市规划师,还是分析路线数据的数据科学家,路线可视化的力量都能为您带来宝贵的洞察。

传统的路线可视化方法往往存在不足,呈现的只是静态的一维视图,无法捕捉现实世界数据的全部复杂性。使用 Python 进入交互式地图的世界——它将彻底改变我们感知和与空间数据交互的方式。

本指南将引导您使用 Python 创建动态交互式路线可视化,并利用 OpenRouteService 的强大功能进行路线计算,以及 Plotly 进行丰富的交互式地图渲染。这种方法的魅力在于其多功能性:它可以应用于任何包含坐标和序列的数据集。无论您处理的是 GPS 轨迹、客户旅程还是配送路线,此方法都能让您的数据栩栩如生。

我们将更进一步,引入区域的概念——可以用颜色编码来标记感兴趣的区域,以便于识别。这一额外的信息层将您的可视化从简单的路线图转变为丰富的多维数据呈现。

在本教程结束时,您将能够:

  • 使用真实世界数据创建交互式地图
  • 根据序列以自定义样式可视化路线
  • 实施区域并使用颜色编码来增强数据表示
  • 添加弹出窗口和图层控件等交互元素

让我们深入研究并释放空间数据的潜力!

先决条件

在深入代码之前,我们先来设置一下 Python 环境并安装必要的软件包。本指南假设您的系统上已经安装了 Python。如果没有,请从python.org下载并安装 Python 。

设置你的 Python 环境

安装所需的软件包:激活虚拟环境后(如果跳过了步骤 1,则在全局 Python 环境中),使用 pip 安装所需的软件包:

pip install pandas openrouteservice plotly

此命令将安装:

  • pandas:用于数据处理和分析
  • openrouteservice:与 OpenRouteService API 交互
  • plotly:用于创建交互式可视化

验证安装:您可以通过尝试在 Python 解释器中导入这些包来验证安装:

import pandas 
import openrouteservice 
import plotly 
print("All packages are successfully installed!")

如果您没有看到任何错误消息,那么就可以开始了!

所需库

一旦安装了软件包,您就可以将它们导入到 Python 脚本中:

import time
import openrouteservice
import plotly.graph_objs as go
import pandas as pd
import plotly.express as px

获取 OpenRouteService API 密钥

该项目的一个重要组成部分是 OpenRouteService API 密钥,它是免费且易于获取的。获取方法如下:

  1. 注册 OpenRouteService:
    -
    访问 OpenRouteService 网站:openrouteservice
    - 单击右上角的“注册”按钮。
    - 您可以使用您的电子邮件或通过您的 GitHub 帐户注册。
  2. 验证您的帐户
    - 如果您使用电子邮件注册,请检查您的收件箱中的验证电子邮件并按照说明进行操作。
  3. 访问您的仪表板
    - 登录后,您将被定向到您的仪表板。
  4. 创建令牌(API 密钥)
    - 在您的仪表板中,查找“添加新 API 密钥”或“创建令牌”选项。
    - 您可能需要为您的令牌提供一个名称并选择您需要的服务(对于此项目,确保选择“方向”)。
    - 单击“创建”或“生成 API 密钥”。
  5. 复制您的 API 密钥
    - 您的新 API 密钥将显示出来。请复制此密钥并妥善保管。

重要提示

  • OpenRouteService 的免费层非常慷慨,适合大多数个人和小型项目。
  • 在撰写本文时,免费套餐通常包括:
  • 每天最多 2,000 个请求
  • 每分钟最多 40 个请求
  • 这些限制对于测试和运行我们的路线可视化项目来说已经足够了。
  • 请务必检查 OpenRouteService 网站上的最新条款和使用限制,因为它们可能会随着时间而改变。
  • 请记住将您的 API 密钥保密,切勿公开分享。

有了 API 密钥,您就可以开始可视化路线了!

数据准备

在本教程中,我们将使用包含以下列的真实示例数据集:

  • 顺序:路线中点的顺序
  • 纬度:每个点的纬度坐标
  • 经度:每个点的经度坐标
  • ZoneID:不同区域或地区的标识符

以下是我们的数据示例:

# Sample data
data = {
    'Sequence': [1, 2, 3, 4, 5, 161, 162, 163, 164, 165],
    'Latitude': [47.707439, 47.706535, 47.708466, 47.708198, 47.708568, 
                 47.733784, 47.734829, 47.735678, 47.719747],
    'Longitude': [-122.201863, -122.201832, -122.204317, -122.206690, -122.206190, 
                  -122.191986, -122.191846, -122.190102, -122.189500],
    'ZoneID': ['C-17.2C', 'C-17.2C', 'C-17.2C', 'C-17.3C', 'C-17.3C', 
               'C-18.3G', 'C-18.3G', 'C-18.3G', 'C-18.2J']
}

这将输出:

Sequence   Latitude    Longitude   ZoneID
0         1  47.707439  -122.201863  C-17.2C
1         2  47.706535  -122.201832  C-17.2C
2         3  47.708466  -122.204317  C-17.2C
3         4  47.708198  -122.206690  C-17.3C
4         5  47.708568  -122.206190  C-17.3C
5       161  47.733784  -122.191986  C-18.3G
6       162  47.734829  -122.191846  C-18.3G
7       163  47.735678  -122.190102  C-18.3G
8       164  47.719747  -122.189500  C-18.2J

该数据集表示一条包含 164 个点的路线(为简洁起见,我们在此显示一个子集),每个点都有一个唯一的序列号、纬度和经度坐标以及一个区域标识符。

理解关键概念

在深入研究代码之前,让我们先澄清一下可视化中使用的三个重要概念:

1. RouteID:特定路线的唯一标识符。它可以表示:

  • 每日配送路线或配送服务中司机指定的路径
  • 公共交通中的公交车或火车线路号码
  • 物流系统中的货运编号或卡车行程 ID
  • 旅游应用中的定制旅游或旅行行程

2. Sequence:表示沿途站点的顺序:

  • 每个序列号对应路线上的一个站点
  • 通常从 1 开始,然后每次停止时递增
  • 每个站点都有相关的经纬度坐标

3. ZoneID:用于对路线的不同段进行分类或分组的可选标识符:

  • 可以表示社区、票价区、地形类型或路线的任何逻辑划分
  • 用于在可视化中对路线的不同部分进行颜色编码
  • 区域之间的过渡被突出显示,为路线变化提供视觉提示

数据预处理

在可视化数据之前,让我们进行一些基本的预处理:

  1. 确保序列列的顺序正确。
  2. 检查是否有任何缺失值。
  3. 将 ZoneID 转换为分类类型以便高效处理。
# Sort by Sequence
df = df.sort_values('Sequence')
# Check for missing values
print(df.isnull().sum())
# Convert ZoneID to categorical
df['ZoneID'] = df['ZoneID'].astype('category')
# Get basic statistics
print(df.describe())

此预处理步骤可确保我们的数据干净且易于可视化。在下一节中,我们将使用此准备好的数据集通过 Folium 创建交互式地图。

函数visualize_route

让我们分解一下主要函数,visualize_route它需要三个参数:

  • df:包含所有路线数据的 pandas DataFrame
  • route_id:要可视化的具体路线 ID
  • api_key:您的 OpenRouteService API 密钥
def visualize_route(master_df, route_id, api_key):
    # Filter the dataframe for the specific route
    df = master_dataframe[(master_dataframe['RouteID']== route_id) & (master_dataframe['Sequence']!=0)]
    
    # Create a dictionary to assign a unique color to each ZoneID using brighter colors
    unique_zones = df['ZoneID'].unique()
    zone_colors = {zone: px.colors.qualitative.Set1[i % len(px.colors.qualitative.Set1)] for i, zone in enumerate(unique_zones)}

    # Initialize OpenRouteService client
    client = openrouteservice.Client(key=api_key)

    def get_route(client, start, end, retries=5, delay=10):
        for i in range(retries):
            try:
                return client.directions(coordinates=[start, end], profile='driving-car', format='geojson')
            except openrouteservice.exceptions.ApiError as e:
                if 'rate limit' in str(e).lower():
                    print(f"Rate limit exceeded. Retrying in {delay} seconds.")
                    time.sleep(delay)
                else:
                    raise e
        raise Exception(f"Failed to get route after {retries} retries.")

    # Create the map visualization
    fig = go.Figure()

    # Add the route lines
    for i in range(len(df) - 1):
        start = (df.iloc[i]['Longitude'], df.iloc[i]['Latitude'])
        end = (df.iloc[i + 1]['Longitude'], df.iloc[i + 1]['Latitude'])
        
        if pd.notnull(start[0]) and pd.notnull(start[1]) and pd.notnull(end[0]) and pd.notnull(end[1]):
            route = get_route(client, start, end)
            geojson = route['features'][0]['geometry']
            coordinates = geojson['coordinates']
            lons, lats = zip(*coordinates)
            
            color = zone_colors[df.iloc[i]['ZoneID']] if df.iloc[i]['ZoneID'] == df.iloc[i+1]['ZoneID'] else 'black'
            
            fig.add_trace(go.Scattermapbox(
                mode="lines",
                lon=lons,
                lat=lats,
                line=dict(width=3, color=color),
                hoverinfo='none',
                showlegend=False
            ))

    # Add the stops with sequence numbers
    fig.add_trace(go.Scattermapbox(
        lat=df['Latitude'],
        lon=df['Longitude'],
        mode='markers+text',
        marker=go.scattermapbox.Marker(
            size=10,
            color=[zone_colors[z] for z in df['ZoneID']],
            showscale=False
        ),
        text=df['Sequence'].astype(str),
        textposition='top center',
        textfont=dict(size=14, color='black'),
        hoverinfo='text',
        hovertext=df['StopID']
    ))


    # Add a legend for ZoneID colors
    for zone, color in zone_colors.items():
        fig.add_trace(go.Scattermapbox(
            lat=[None], lon=[None],
            mode='markers',
            marker=dict(size=10, color=color),
            legendgroup=zone,
            showlegend=True,
            name=f'Zone {zone}'
        ))

    # Add a legend for black lines
    fig.add_trace(go.Scattermapbox(
        lat=[None], lon=[None],
        mode='lines',
        line=dict(width=3, color='black'),
        legendgroup='Transition',
        showlegend=True,
        name='Transition between zones'
    ))

    # Update the layout of the map
    fig.update_layout(
        mapbox=dict(
            style="open-street-map",
            zoom=12,
            center=dict(lon=df['Longitude'].mean(), lat=df['Latitude'].mean())
        ),
        height=1000,
        width=1200,
        margin={"r":0,"t":0,"l":0,"b":0},
        legend=dict(
            yanchor="top",
            y=0.99,
            xanchor="left",
            x=0.01
        ),
        title=f"Route Visualization for {route_id}"
    )

    # Show the plot
    fig.show()

    # Save the figure to an HTML file
    filename = f"Route_{route_id}.html"
    fig.write_html(filename)
    print(f"Map saved to {filename}")

使用函数

要使用此函数,请使用您的数据调用它:

<span style="background-color:#f9f9f9"><span style="color:#242424">可视化路线(df,<span style="color:#c41a16">'YOUR_ROUTE_ID'</span>,<span style="color:#c41a16">'YOUR_OPENROUTESERVICE_API_KEY'</span>)</span></span>

'YOUR_ROUTE_ID'用您想要可视化的特定路线和'YOUR_OPENROUTESERVICE_API_KEY'实际的 API 密钥进行替换。

示例输出

为了让您具体了解该visualize_route函数产生的结果,让我们看一下其中一条路线的示例输出。

使用特定的 RouteID 运行该函数后,您将获得一个交互式 HTML 文件。以下是可视化效果的快照:

将显示缩放图像

示例输出:包含多个区域的可视化路线

此可视化的主要特点:

  1. 颜色编码路线:主路线根据 ZoneID 进行颜色编码。在此示例中,您可以看到不同的颜色代表路线沿途的各个区域。
  2. 编号站点:路线上的每个站点都标有一个数字,代表站点的顺序。
  3. 区域转换:黑线表示不同区域之间的转换,可以轻松识别区域变化发生的位置。
  4. 交互元素:虽然在此静态图像中不可见,但 HTML 输出允许缩放、平移和悬停在点上以获取更多信息。
  5. 图例:左侧有一个图例,显示每种颜色所代表的区域。

在这个特定的路线中:

  • 路线从左下角开始(以站点编号 1 表示)并穿过各个区域。
  • 您可以看到区域之间的清晰过渡,以黑线标记。
  • 该路线覆盖相当大的区域,穿过多个街区或区域(以不同的颜色表示)。
  • 此路线约有 165 个站点,每个站点均按顺序编号。

通过这种可视化,您可以轻松实现以下操作:

  • 了解路线的整体结构和流程
  • 确定路线的哪些部分属于哪些区域
  • 查看区域转换发生的位置
  • 了解路线的复杂性和覆盖范围

请记住,实际的 HTML 输出是交互式的,可以比静态图像中显示的更详细地探索路线。

适应不同的用例

此可视化功能非常灵活,可以适应各种场景:

1.送货路线

  • df可以包含一天或一周的所有送货路线
  • route_id可能是特定司机的路线或送货区域
  • ZoneID可以代表不同的社区或配送区域

2.公共交通

  • df可以保存一个城市所有公交或火车线路的数据
  • route_id代表特定的行号
  • ZoneID可能指示票价区或不同的城市区域

3.物流和供应链

  • df可能包含所有货运或卡车路线的数据
  • route_id可能是特定的装运编号或卡车行程
  • ZoneID可以表示不同类型的地形或管辖范围

4.定制旅游或行程

  • df可以存储各种旅游路线或旅行行程
  • route_id代表特定的旅游或旅行计划
  • ZoneID可能代表不同的景点或主题

自定义 ZoneID 可视化

您可以进一步自定义可视化中 ZoneID 的使用方式:

  1. 颜色分配:修改zone_colors字典以将特定颜色分配给某些区域。
  2. 区域过渡:调整区域之间绘制线条的逻辑以使用渐变或不同的样式。
  3. 基于区域的注释:根据区域变化添加额外的注释或标记。

结论

此实现提供了一种强大而灵活的方法,可以使用真实数据可视化路线。它提供:

  1. 根据区域颜色编码的路线
  2. 区域转换清晰指示
  3. 每个站点的序列号
  4. 具有缩放和平移功能的交互式地图
  5. 使用 OpenRouteService 自动计算站点之间的路线
  6. 轻松解释区域和过渡的图例
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐