2013-03-27 2 views
3

matplotlib 경로의 선폭을 그림 줌/스케일 수준에 연결할 수 있습니까?matplotlib 경로 선폭을 그림 확대/축소에 연결

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

+0

hmmm .... plt.quiver가 나를 위해이 일을하지 않습니다. – waqy

+1

@askewchan -'quiver'는 경로 대신에 폴리곤을 사용하기 때문에 (즉, 화살표 대신에 획이없고 대신 다각형입니다). –

답변

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

Bezier Curve를 매끈하게 그리거나 확장/버퍼링하는 것이 가능합니까? – waqy

+0

@waqy - 아니요, '매끈한'은 선을 선형 선으로 처리합니다. 당신은 그들을 대략해야 할 것입니다. matplotlib 만 사용하여 콜백 기반 방식으로 답변을 업데이트했습니다 (종횡비가 1로 설정되어 있지 않으면 정확히 맞습니다). –

+0

감사합니다. 콜백 기반 스키마가 완벽하게 작동합니다 (곡선 및 텍스트도 마찬가지입니다). – waqy

관련 문제