Python matplotlib连接到地物缩放的路径线宽
是否可以将matplotlib路径的线宽与地物缩放/比例级别绑定 我正在绘制一张地图,其中matplotlib路径(带有贝塞尔曲线)在地图上绘制道路。放大后,我想放大路径的宽度 在附加脚本中,多边形近似可以正确缩放,但路径(红线)无法缩放(宽度) 是否可以通过回调将线宽绑定到某些缩放变换和重画Python matplotlib连接到地物缩放的路径线宽,python,python-3.x,matplotlib,Python,Python 3.x,Matplotlib,是否可以将matplotlib路径的线宽与地物缩放/比例级别绑定 我正在绘制一张地图,其中matplotlib路径(带有贝塞尔曲线)在地图上绘制道路。放大后,我想放大路径的宽度 在附加脚本中,多边形近似可以正确缩放,但路径(红线)无法缩放(宽度) 是否可以通过回调将线宽绑定到某些缩放变换和重画 import matplotlib.pyplot as plt from matplotlib.path import Path import matplotlib.patches as patches
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
import numpy as np
def main():
ax = plt.subplot(111)
verts = np.array([ (0., 0.), (0.5, .5), (1., 0.8), (0.8, 0.)])
codes = np.array([Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.LINETO ])
# Can this curve have zoomable width
path = Path(verts, codes)
patch = patches.PathPatch(path, fc='none', color='r', lw=4, zorder=3)
ax.add_patch(patch)
ax.plot(verts[:,0], verts[:,1], 'o--', lw=2, color='k', zorder=2)
# these will be polygonal approx that will have proper zoom
v=np.array([]).reshape((-1,2))
c=[]
for i in range(len(verts)-1):
vtmp, ctmp = line2poly(verts[[i,i+1],:],0.03)
v = np.vstack( (v,vtmp) )
c = np.concatenate( (c,ctmp) )
path_zoom = Path(v,c)
patch_zoom = patches.PathPatch(path_zoom, fc='r', ec='k', zorder=1, alpha=0.4)
ax.add_patch(patch_zoom)
ax.set_xlim(-0.1, 1.1)
ax.set_ylim(-0.1, 1.1)
plt.show()
def line2poly(line, width):
dx,dy = np.hstack(np.diff(line,axis=0)).tolist()
theta = np.arctan2(dy,dx)
print(np.hstack(np.diff(line,axis=0)).tolist())
print(np.degrees(theta))
s = width/2 * np.sin(theta)
c = width/2 * np.cos(theta)
trans = np.array([(-s,c),(s,-c),(s,-c),(-s,c)])
verts = line[[0,0,1,1],:]+trans
verts = np.vstack((verts, verts[0,:]))
codes = np.array([Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
return verts,codes
if __name__=='__main__':
main()
据我所知,在matplotlib中无法做到这一点,因为直线的笔划宽度不能直接与数据坐标关联。(正如您所提到的,您可以将回调连接到draw事件并完成此操作。但是,这将导致巨大的性能损失。) 但是,一个快速的解决方法是使用
shapely
通过缓冲街道路径来生成多边形
举个简单的例子:
import shapely.geometry
import descartes
import matplotlib.pyplot as plt
lines = ([(0, 0), (1, 0), (0, 1)],
[(0, 0), (1, 1)],
[(0.5, 0.5), (1, 0.5)],
)
lines = shapely.geometry.MultiLineString(lines)
# "0.05" is the _radius_ in data coords, so the width will be 0.1 units.
poly = lines.buffer(0.05)
fig, ax = plt.subplots()
patch = descartes.PolygonPatch(poly, fc='gray', ec='black')
ax.add_artist(patch)
# Rescale things to leave a bit of room around the edges...
ax.margins(0.1)
plt.show()
如果确实希望采用回调路由,可以执行以下操作:
import matplotlib.pyplot as plt
def main():
lines = ([(0, 0), (1, 0), (0, 1)],
[(0, 0), (1, 1)],
[(0.5, 0.5), (1, 0.5)],
)
fig, ax = plt.subplots()
artists = []
for verts in lines:
x, y = zip(*verts)
line, = ax.plot(x, y)
artists.append(line)
scalar = StrokeScalar(artists, 0.1)
ax.callbacks.connect('xlim_changed', scalar)
ax.callbacks.connect('ylim_changed', scalar)
# Rescale things to leave a bit of room around the edges...
ax.margins(0.05)
plt.show()
class StrokeScalar(object):
def __init__(self, artists, width):
self.width = width
self.artists = artists
# Assume there's only one axes and one figure, for the moment...
self.ax = artists[0].axes
self.fig = self.ax.figure
def __call__(self, event):
"""Intended to be connected to a draw event callback."""
for artist in self.artists:
artist.set_linewidth(self.stroke_width)
@property
def stroke_width(self):
positions = [[0, 0], [self.width, self.width]]
to_inches = self.fig.dpi_scale_trans.inverted().transform
pixels = self.ax.transData.transform(positions)
points = to_inches(pixels) * 72
return points.ptp(axis=0).mean() # Not quite correct...
main()
plt.quiver
以某种方式做到了这一点,但我不知道如何利用这些信息:Phmmm。。。。plt.quiver不适合我。@askewchan-quiver
这样做是因为它使用多边形而不是路径(即箭头没有笔划,而是多边形)。啊,谢谢@Joe,这很有意义。有可能以匀称的方式绘制bezier曲线并放大/缓冲它们吗?@waqy-没有,shapely
将直线视为直线段。你得估计一下。我用基于回调的方法更新了我的答案,只使用matplotlib(除非纵横比设置为1,否则它完全正确)。谢谢。您基于回调的方案非常有效(也适用于曲线和文本)。很高兴它有帮助!在某些情况下,它有一些奇怪之处,我不太明白。此外,在我之前的评论中,我的意思是说“除非纵横比设置为1,否则它并不完全正确”。更光明的是,我提到的“巨大的性能损失”结果是完全无关紧要的。这就是我在尝试之前猜测的结果!