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.