【问题标题】:Plotly: How to plot arrow line (directed graph)?Plotly:如何绘制箭头线(有向图)?
【发布时间】:2022-01-13 10:10:01
【问题描述】:

我想在节点之间绘制一条箭头线来指示路线的方向。我找不到这样做的内置函数。有什么方法可以完成这项工作吗?

我的代码:

import plotly.graph_objects as go
import plotly.io as pio
import plotly

lon1 = [113.076843, 113.154191, 113.737213, 113.842405, 114.244183 ]
lat1 = [23.10993, 23.218533, 23.047626, 22.987975, 22.601581 ]

lon2 = [113.364738, 113.664108, 113.661705,114.244183]
lat2 = [22.997112, 22.878038, 22.869216, 22.601581]

lon_trip1 = lon1
lat_trip1 = lat1

lon_trip2 = lon2
lat_trip2 = lat2

fig = go.Figure()
fig.add_trace(go.Scattermapbox(
    mode="markers+lines",
    lon=lon_trip1,
    lat=lat_trip1,
    name="trip1", marker={'size': 10}))

fig.add_trace(
    go.Scattermapbox(
        mode="markers+lines",
        lon=lon_trip2,
        lat=lat_trip2,
        name="trip2",
        marker={'size': 10}))

fig.update_layout(
    margin={'l': 113, 't': 24, 'b': 22, 'r': 115},
    mapbox=dict(style='carto-positron',
                center=go.layout.mapbox.Center(lon=113.664, lat=22.878),
                pitch=0,
                zoom=8)
)


pio.write_image(fig,'C:/Users/Jie/Desktop/plot.png',width=1980, height=1080)
#plotly.offline.plot(fig, filename='C:/Users/user/Desktop/plot' + '.html')
fig.show()

另外,我想在真实世界地图上进一步添加networkx图。是否可以使用 plotly 将地图图层添加到原始 networkx 图中?

完整代码在这里: Plot Networkx graph on a real world map

【问题讨论】:

    标签: plotly mapbox


    【解决方案1】:
    • 您可以将 geojson 图层添加到 ma​​pbox 人物
    • 这种方法会在每条线段的中间创建一个标记以显示行进方向
    • 使用高中三角学旋转标记以计算行进角度
    • 具有基于 cmets 的扩展解决方案。现在可以使用place 参数请求放置。图片显示beyond选项包括center、end、beyond

    代码

    
    import plotly.graph_objects as go
    import plotly.express as px
    import plotly.io as pio
    import plotly
    import math
    import shapely.geometry
    from shapely.affinity import rotate as R, translate as T
    
    lon1 = [113.076843, 113.154191, 113.737213, 113.842405, 114.244183]
    lat1 = [23.10993, 23.218533, 23.047626, 22.987975, 22.601581]
    
    lon2 = [113.364738, 113.664108, 113.661705, 114.244183]
    lat2 = [22.997112, 22.878038, 22.869216, 22.601581]
    
    lon_trip1 = lon1
    lat_trip1 = lat1
    
    lon_trip2 = lon2
    lat_trip2 = lat2
    
    fig = go.Figure()
    fig.add_trace(
        go.Scattermapbox(
            mode="markers+lines",
            lon=lon_trip1,
            lat=lat_trip1,
            name="trip1",
            marker={"size": 10},
        )
    )
    
    fig.add_trace(
        go.Scattermapbox(
            mode="markers+lines",
            lon=lon_trip2,
            lat=lat_trip2,
            name="trip2",
            marker={"size": 10},
        )
    )
    
    # create a mapbox layer that are arrows that show direction between sequence of lat / lon pairs
    def direction(lat1, lon1, marker=None, color="yellow", place="center"):
        # default marker is a left pointing arrow, centred as 0,0
        # it may be necessary to adjust sizing
        if marker is None:
            m = R(shapely.geometry.Polygon([(0, 0), (0, 1), (0.5, 0.5)]), 0)
            m = shapely.affinity.scale(m, xfact=0.05, yfact=0.05)  # size appropriately
            m = T(m, -m.centroid.x, -m.centroid.y)
        else:
            m = marker
    
        def place_marker(m, x1, x0, y1, y0, place=place):
            theta = math.atan2(x1 - x0, y1 - y0)
            if place == "center":
                p = dict(
                    xoff=(y1 - y0) / 2 + y0,
                    yoff=(x1 - x0) / 2 + x0,
                )
            elif place == "end":
                p = dict(xoff=y1, yoff=x1)
            elif place == "beyond":
                H = .05
                p = dict(xoff=y1 + math.cos(theta)*H, yoff=x1 + math.sin(theta)*H)
            return T(R(m, theta, use_radians=True), **p)
    
        return {
            # list of geometries rotated based on direction and centred on line between pairs of points
            "source": shapely.geometry.MultiPolygon(
                [
                    place_marker(m, x1, x0, y1, y0)
                    for x0, x1, y0, y1 in zip(lat1, lat1[1:], lon1, lon1[1:])
                ]
            ).__geo_interface__,
            "type": "fill",
            "color": color,
            "below":"traces"
        }
    
    
    fig.update_layout(
        margin={"l": 113, "t": 24, "b": 22, "r": 115},
        mapbox=dict(
            style="carto-positron",
            center=go.layout.mapbox.Center(lon=113.664, lat=22.878),
            pitch=0,
            zoom=8,
            layers=[
                direction(t.lat, t.lon, marker=None, color=c, place="beyond")
                for t, c in zip(fig.data, px.colors.qualitative.Plotly)
            ],
        ),
    )
    

    【讨论】:

    • 感谢您的帮助。是否可以将箭头定位在行尾而不是中间,就像普通的箭头线一样?
    • 是的 - 已更新以提供这种灵活性。
    • 由于某种原因,我的机器无法安装 geopandas。刚找到这个直接画图的方法:community.plotly.com/t/…不知道行不行。
    • plotly.com/python/map-configuration mapbox 和 geo 使用起来完全不同......你的机器上安装了吗?我已经删除了对 geopandas 的依赖
    • 亲爱的 Rob 非常感谢。这好多了。如何调整箭头以使其不与节点重叠,即(将箭头移到节点前面而不在它上面)?我在节点上显示了数字,如果它们重叠,将被覆盖。