2010-07-23 11 views
12

Io uso plot(x,y,'r') per tracciare un cerchio rosso. xey sono array tali che quando accoppiati come (x, y) e tracciati, tutti i punti formano una linea di cerchio.Fill OUTSIDE of polygon | Array di maschere in cui gli indici sono oltre un limite circolare?

fill(x,y,'r') traccia un cerchio rosso riempito (o colorato) di rosso.

Come posso mantenere il cerchio bianco all'interno, ma riempire all'esterno del cerchio fino ai limiti dell'asse?

Ho esaminato l'utilizzo di fill_between(x_array, y1_array, y2_array, where) ma dopo un po 'di gioco non credo che funzionerà con i miei array x, y. Ho pensato a fill_between() fuori dal cerchio e all'interno di un quadrato definito dai limiti degli assi, ma non penso che lo fill_between() sia in grado ... Sono sicuro che potrei trasformarlo in un tipo di problema integrale con delta x e delta y andando a zero, ma non sono disposto a farlo.

Se qualcuno può vedere che mi manca qualcosa con fill_between() per favore fatemelo sapere.

Tutto ciò che devo davvero fare è mascherare i numeri in un array 2D che si trovano oltre questo limite del cerchio creato con x e y, in modo tale che quando l'array 2D viene visualizzato come un tracciato di colore o contorno, all'interno del cerchio ci sarà l'immagine, e all'esterno sarà cancellato il bianco.

Ciò può essere ottenuto con una tecnica di mascheramento dell'array 2D? Ti piace usare masked_where()? Non l'ho ancora esaminato, ma lo farò.

Qualche idea? Grazie

Modifica 1: Ecco cosa ho il permesso di mostrare che penso spiegherà il mio problema.

from pylab import * 
from matplotlib.path import Path 
from matplotlib.patches import PathPatch 

f=Figure() 
a=f.add_subplot(111) 

# x,y,z are 2d arrays 

# sometimes i plot a color plot 
# im = a.pcolor(x,y,z) 
a.pcolor(x,y,z) 

# sometimes i plot a contour 
a.contour(x,y,z) 

# sometimes i plot both using a.hold(True) 

# here is the masking part. 
# sometimes i just want to see the boundary drawn without masking 
# sometimes i want to see the boundary drawn with masking inside of the boundary 
# sometimes i want to see the boundary drawn with masking outside of the boundary 

# depending on the vectors that define x_bound and y_bound, sometimes the boundary 
# is a circle, sometimes it is not. 

path=Path(vpath) 
patch=PathPatch(path,facecolor='none') 
a.add_patch(patch) # just plots boundary if anything has been previously plotted on a 
if ('I want to mask inside'): 
    patch.set_facecolor('white') # masks(whitens) inside if pcolor is currently on a, 
    # but if contour is on a, the contour part is not whitened out. 
else: # i want to mask outside 
    im.set_clip_path(patch) # masks outside only when im = a.pcolor(x,y,z) 
    # the following commands don't update any masking but they don't produce errors? 
    # patch.set_clip_on(True) 
    # a.set_clip_on(True) 
    # a.set_clip_path(patch) 

a.show() 

risposta

13

Tutto quello che sto veramente bisogno di fare è maschera i numeri in una matrice 2D che sono trova al di là di questo confine del cerchio creata con xey, in modo tale che quando l'array 2D è visualizzato come un colore trama, o contorno, all'interno del cerchio sarà l'immagine, e all'esterno sarà out bianco.

si hanno due opzioni:

In primo luogo, è possibile utilizzare una matrice mascherato per le immagini. Questo è più complicato ma un po 'più sicuro. Per mascherare una matrice al di fuori di un cerchio, generare una mappa della distanza dal punto centrale e mascherare dove la distanza è maggiore del raggio.

L'opzione più semplice è ritagliare le aree esterne della patch con im.set_clip_path() dopo aver tracciato l'immagine.

Vedere this example from the matplotlib gallery. Sfortunatamente, con alcune asce (assi non cartesiani) questa può essere un po 'problematica, secondo la mia esperienza. In ogni altro caso dovrebbe funzionare perfettamente, però.

Modifica: Incidentalmente, this is how to do what you originally asked: traccia un poligono con un buco all'interno. Se vuoi semplicemente mascherare un'immagine, però, stai meglio con una delle due opzioni sopra.

Edit2: solo per dare un rapido esempio di entrambi i modi ...

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.patches as patches 

def main(): 
    # Generate some random data 
    nx, ny = 100, 100 
    data = np.random.random((ny,nx)) 

    # Define a circle in the center of the data with a radius of 20 pixels 
    radius = 20 
    center_x = nx // 2 
    center_y = ny // 2 

    plot_masked(data, center_x, center_y, radius) 
    plot_clipped(data, center_x, center_y, radius) 
    plt.show() 

def plot_masked(data, center_x, center_y, radius): 
    """Plots the image masked outside of a circle using masked arrays""" 
    # Calculate the distance from the center of the circle 
    ny, nx = data.shape 
    ix, iy = np.meshgrid(np.arange(nx), np.arange(ny)) 
    distance = np.sqrt((ix - center_x)**2 + (iy - center_y)**2) 

    # Mask portions of the data array outside of the circle 
    data = np.ma.masked_where(distance > radius, data) 

    # Plot 
    plt.figure() 
    plt.imshow(data) 
    plt.title('Masked Array') 

def plot_clipped(data, center_x, center_y, radius): 
    """Plots the image clipped outside of a circle by using a clip path""" 
    fig = plt.figure() 
    ax = fig.add_subplot(111) 

    # Make a circle 
    circ = patches.Circle((center_x, center_y), radius, facecolor='none') 
    ax.add_patch(circ) # Plot the outline 

    # Plot the clipped image 
    im = ax.imshow(data, clip_path=circ, clip_on=True) 

    plt.title('Clipped Array') 

main() 

enter image description here enter image description here

Edit 2: Tracciare un poligono maschera sopra la trama originale: Ecco un po 'più in dettaglio su come tracciare un poligono che maschera tutto al di fuori di esso sopra il grafico corrente. Apparentemente, non c'è un modo migliore per ritagliare grafici di contorno (che potrei trovare, comunque ...).

import numpy as np 
import matplotlib.pyplot as plt 

def main(): 
    # Contour some regular (fake) data 
    grid = np.arange(100).reshape((10,10)) 
    plt.contourf(grid) 

    # Verticies of the clipping polygon in counter-clockwise order 
    # (A triange, in this case) 
    poly_verts = [(2, 2), (5, 2.5), (6, 8), (2, 2)] 

    mask_outside_polygon(poly_verts) 

    plt.show() 

def mask_outside_polygon(poly_verts, ax=None): 
    """ 
    Plots a mask on the specified axis ("ax", defaults to plt.gca()) such that 
    all areas outside of the polygon specified by "poly_verts" are masked. 

    "poly_verts" must be a list of tuples of the verticies in the polygon in 
    counter-clockwise order. 

    Returns the matplotlib.patches.PathPatch instance plotted on the figure. 
    """ 
    import matplotlib.patches as mpatches 
    import matplotlib.path as mpath 

    if ax is None: 
     ax = plt.gca() 

    # Get current plot limits 
    xlim = ax.get_xlim() 
    ylim = ax.get_ylim() 

    # Verticies of the plot boundaries in clockwise order 
    bound_verts = [(xlim[0], ylim[0]), (xlim[0], ylim[1]), 
        (xlim[1], ylim[1]), (xlim[1], ylim[0]), 
        (xlim[0], ylim[0])] 

    # A series of codes (1 and 2) to tell matplotlib whether to draw a line or 
    # move the "pen" (So that there's no connecting line) 
    bound_codes = [mpath.Path.MOVETO] + (len(bound_verts) - 1) * [mpath.Path.LINETO] 
    poly_codes = [mpath.Path.MOVETO] + (len(poly_verts) - 1) * [mpath.Path.LINETO] 

    # Plot the masking patch 
    path = mpath.Path(bound_verts + poly_verts, bound_codes + poly_codes) 
    patch = mpatches.PathPatch(path, facecolor='white', edgecolor='none') 
    patch = ax.add_patch(patch) 

    # Reset the plot limits to their original extents 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 

    return patch 

if __name__ == '__main__': 
    main() 

Clipped contour plot

+0

Grazie Joe. il set_clip_path sembra la mia migliore opzione, ma se volessi andare con la prima opzione, dovrei usare il metodo 'MaskedArray()'? – AmyS

+0

@AmyS - Sì, ho aggiunto un esempio per mostrare entrambi i modi di fare le cose. Spero che sia d'aiuto! –

+0

Grazie per gli extra Joe. Intersecante e utile, ma per l'applicazione corrente su cui sto lavorando, il mio limite è definito da due vettori che a volte associati formano un cerchio, a volte no. Io uso 'pcolor()' perché il mio asse è definito da 2 array 2d che non sono sempre cartesiani, e non riesco a capire come gestirlo con 'imshow()'. Fortunatamente, 'set_clip_plath (patch)' è un attr. per 'pcolor()', ma non per 'contour()' o 'plot()' :(Se sei ancora interessato, dare un'occhiata a Modifica 1 sopra della mia domanda e vedere se posso aggiungere il mascheramento patch ad asse senza colore predefinito? – AmyS

2

Nota: Questa risposta utilizza la sintassi MATLAB, in quanto la questione è stato originariamente etichettato come tale. Tuttavia, anche se stai usando matplotlib in Python, il concetto dovrebbe essere lo stesso anche se la sintassi è leggermente diversa.

Una possibilità che hai è di fare un poligono che sembra avere un buco in esso, ma in realtà solo ha due dei suoi bordi avvolgere attorno uno spazio vuoto e toccante. È possibile farlo creando un set di coordinate x e che seguono il bordo del cerchio, quindi tracciate dal bordo del cerchio al bordo di un quadrato di delimitazione, quindi tracciate attorno al bordo di quel quadrato e tornate al bordo del cerchio lungo la stessa linea. Ecco un esempio con un cerchio unitario e un quadrato 4 per 4 centrata nell'origine:

theta = linspace(0,2*pi,100);  %# A vector of 100 angles from 0 to 2*pi 
xCircle = cos(theta);    %# x coordinates for circle 
yCircle = sin(theta);    %# y coordinates for circle 
xSquare = [2 2 -2 -2 2 2];   %# x coordinates for square 
ySquare = [0 -2 -2 2 2 0];   %# y coordinates for square 
hp = fill([xCircle xSquare],... %# Plot the filled polygon 
      [yCircle ySquare],'r'); 
axis equal       %# Make axes tick marks equal in size 

E qui è la figura si dovrebbe vedere:

alt text

Avviso la linea a destra che unisce i bordi del cerchio e del quadrato. È qui che due bordi del poligono rosso si incontrano e si toccano. Se non vuoi che le linee del bordo siano visibili, puoi cambiare il colore in modo che sia uguale al colore di riempimento del poligono in questo modo:

set(hp,'EdgeColor','r'); 
+0

Sicuramente MATLAB ha un modo per tracciare i poligoni con i fori direttamente ?? Poi di nuovo, non riesco a trovarlo neanche ... Ecco come lo gestisce Matplotlib: http://matplotlib.sourceforge.net/examples/api/donut_demo.html –

+0

Grazie per le idee ragazzi. Sembra che il modo più conveniente sia quello di creare una patch dalla classe path.Path e di lavorare da lì come nell'esempio sopra, o esempio dato in un'altra risposta usando set_clip_path() – AmyS

Problemi correlati