简体   繁体   中英

How can draw a line in matplotlib so that the edge (not the center) of the drawn line follows the plotted data?

I'm working on a figure to show traffic levels on a highway map. The idea is that for each highway segment, I would plot two lines - one for direction. The thickness of each line would correspond to the traffic volume in that direction. I need to plot the lines so that the left edge (relative to driving direction) of the drawn line follows the shape of the highway segment. I would like to specify the shape in data coordinates, but I would like to specify the thickness of the line in points.

My data is like this:

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

where, for example, ((5,10),(-7,2),(8,9)) is a sequence of x,y values giving the shape of a highway segment, and (210,320) is traffic volumes in the forward and reverse direction, respectively

Looks matter: the result should be pretty.

I figured out a solution using matplotlib.transforms.Transform and shapely.geometry.LineString.parallel_offset .

Note that shapely's parallel_offset method can sometimes return a MultiLineString , which is not handled by this code. I've changed the second shape so it does not cross over itself to avoid this problem. I think this problem would happen rarely happen in my application.

Another note: the documentation for matplotlib.transforms.Transform seems to imply that the array returned by the transform method must be the same shape as the array passed as an argument, but adding additional points to plot in the transform method seems to work here.

#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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM