如何在matplotlib中绘制一条线,使得线的边缘(而不是中心)跟随绘制的数据?

1 投票
1 回答
1920 浏览
提问于 2025-04-17 13:41

我正在制作一个图,目的是展示高速公路上的交通流量。我的想法是,对于每一段高速公路,我会画两条线——一条表示一个方向。每条线的粗细会根据那个方向的交通量来决定。我需要把这些线画得让它们的左边缘(相对于行驶方向)跟高速公路的形状一致。我希望能用数据坐标来指定形状,但想用点数来指定线的粗细。

我的数据是这样的:

[[((5,10),(-7,2),(8,9)),(210,320)],
 [((8,4),(9,1),(8,1),(11,4)),(2000,1900)],
 [((12,14),(17,14)),(550,650)]]

比如说,((5,10),(-7,2),(8,9))是一系列的x,y值,表示高速公路段的形状,而(210,320)则是前进和反向的交通量。

外观很重要:最终的效果应该要好看。

1 个回答

0

我找到了一种解决方案,使用了 matplotlib.transforms.Transformshapely.geometry.LineString.parallel_offset

需要注意的是,shapely 的 parallel_offset 方法有时会返回一个 MultiLineString,而这个代码并没有处理这种情况。为了避免这个问题,我把第二个形状改了一下,确保它不会自交。我觉得在我的应用中,这种问题发生的几率很小。

还有一点要提:matplotlib.transforms.Transform 的文档似乎暗示 transform 方法返回的数组必须和传入的数组形状相同,但在 transform 方法中添加额外的点进行绘制似乎是可行的。

#matplotlib version 1.1.0
#shapely version 1.2.14
#Python 2.7.3

import matplotlib.pyplot as plt
import shapely.geometry
import numpy
import matplotlib.transforms


def get_my_transform(offset_points, fig):
    offset_inches = offset_points / 72.0
    offset_dots = offset_inches * fig.dpi

    class my_transform(matplotlib.transforms.Transform):        

        input_dims = 2
        output_dims = 2
        is_separable = False
        has_inverse = False

        def transform(self, values):
            l = shapely.geometry.LineString(values)
            l = l.parallel_offset(offset_dots,'right')
            return numpy.array(l.xy).T

    return my_transform()


def plot_to_right(ax, x,y,linewidth, **args):

    t = ax.transData + get_my_transform(linewidth/2.0,ax.figure)

    ax.plot(x,y, transform = t,
            linewidth = linewidth,
            solid_capstyle = 'butt',
            **args)


data = [[((5,10),(-7,2),(8,9)),(210,320)],
 [((8,4),(9,1),(8,1),(1,4)),(2000,1900)],
 [((12,14),(17,16)),(550,650)]]


fig = plt.figure()
ax = fig.add_subplot(111)


for shape, volumes in data:

    x,y = zip(*shape)
    plot_to_right(ax, x,y, volumes[0]/100., c = 'blue')
    plot_to_right(ax, x[-1::-1],y[-1::-1], volumes[1]/100., c = 'green')
    ax.plot(x,y, c = 'grey', linewidth = 1)


plt.show()
plt.close()

撰写回答