2013-03-27 82 views
3

是否可以將matplotlib路徑的線寬與數字縮放/比例級別綁定?matplotlib路徑線寬連接到圖zoom

我畫一個地圖,其中matplotlib路徑(與貝塞爾曲線)在地圖上繪製的道路。當在我變焦想路徑的寬度進行放大。

在附加的腳本,多邊形逼近可適當放大,但路徑(紅線)不能放大(寬)。

是否可以將線寬與某個比例轉換相結合並通過回調重繪?

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() 
+0

' plt.quiver'這樣做,但我不知道如何利用這些信息:P – askewchan 2013-03-27 23:18:37

+0

嗯.... plt.quiver不會爲我做這個。 – waqy 2013-03-28 00:50:39

+1

@askewchan - '顫抖'是因爲它使用多邊形而不是路徑(即箭頭沒有中斷,而是多邊形)。 – 2013-03-28 02:18:17

回答

5

據我所知,在matplotlib中沒有辦法做到這一點,因爲行的筆劃寬度不能直接綁定到數據座標。 (正如你所提到的,你可以將一個回調函數連接到繪圖事件並完成這個任務,但是會產生大的性能損失。)

但是,一個快速的解決方法是使用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() 

enter image description here

如果你是想利用回調的路線,你可以做這樣的事情:

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

enter image description here

+0

是否可以繪製貝齊曲線,並將其緩衝? – waqy 2013-03-28 03:47:55

+0

@waqy - 不,''shapely'將線條視爲線段。你必須接近它們。我使用基於回調的方式更新了我的答案,只使用matplotlib(除非長寬比設置爲1,否則它是完全正確的)。 – 2013-03-28 03:49:57

+0

謝謝。您的基於回調的計劃完美運行(對於曲線和文字也是如此)。 – waqy 2013-03-28 20:47:32