I have got a figure with with an axis object.
I would like to use ax.scatter to scatter something into the plot, which is no problem. On top I want to draw 2D array data into the same figure (using ax.imshow for example...):
import numpy as np
from matplotlib import pyplot as plt
vidRes = np.array([[-700, 700], [-500,500]])
dim = np.array([vidRes[0,1] - vidRes[0,0],vidRes[1,1] - vidRes[1,0]])
with plt.style.context('dark_background'):
fg = plt.figure()
fg.set_size_inches((10, 10*dim[1]/dim[0]))
ax = fg.add_subplot(111)
ax.set_aspect('equal')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.set_xlim(vidRes[0,0], vidRes[0,1])
ax.set_ylim(vidRes[1,0], vidRes[1,1])
random = np.random.random(size = (401, 401,),)
ax.imshow(random, cmap = 'hot')
The 2D array has 401x401 entries. Is there a straight forward way to move the imshow object to center it on a given pixel in my 1400x1000 pixel figure? Am I blind to not see it?
The image is drawn into the figure but it is always aligned with the lower left or upper left corner (depending on origin of ax.imshow) to the (0,0) coordinate at the center of my plot, I can't move it anywhere else.
You can use the extent argument. From the docs:
extent : scalars (left, right, bottom, top), optional, default: None
The location, in data-coordinates, of the lower-left and
upper-right corners. If None, the image is positioned such that
the pixel centers fall on zero-based (row, column) indices.
Changing your imshow call to the following line:
ax.imshow(random, cmap = 'hot', extent=[-200.5, 200.5, -200.5, 200.5])
yields:
Related
There is a polygon and I am wondering how can I change color of particular edge of it like figure below.
import matplotlib.pyplot as plt
import numpy as np
## -----------------------Initialize Geometry-----------------------
pixels = 600
my_dpi = 100
coord = [[-150,-200],[300,-200],[300,0],[150,200],[-150,200]]
fig = plt.figure(figsize=( pixels/my_dpi, pixels/my_dpi), dpi=my_dpi)
plt.axes([0,0,1,1])
rectangle = plt.Rectangle((-300, -300), 600, 600, fc='k')
plt.gca().add_patch(rectangle)
polygon = plt.Polygon(coord,fc='w')
plt.gca().add_patch(polygon)
plt.axis('off')
plt.axis([-300,300,-300,300])
plt.savefig('figure1/5.jpg',dpi=my_dpi)
The easiest way to do this would be to simply plot a line between the two relevant vertices of the polygon, i.e.
plt.plot([coords[0,0], coords[-1,0]], [coords[0,1], coords[-1,1]], color='r', lw=5)
Would give you
Although I recommend adding a border to the polygon with the same width as this line of the same color as the facecolor:
polygon = plt.Polygon(coord,fc='w',ec='w',lw=5)
As a way to make the red line appear flush. You can change which edge is colored you simply change the indices of coords[i,j] in plt.plot() and as long as the indices are adjacent (with wrapping - so last index and first index are adjacent) the line drawn will be an edge and not a diagonal.
Also note you can shorten the plotting command by using slices or a helper function but I have neglected this for the sake of being explicit.
I have an image in python. It's a map of california, and I need to place some point on this map.
The coordonate of each point are retrieve from a csv. But the value of each coordinate are in latitude/longitude. So, i need to convert it to the dimension of my picture.
So, here's is the description of my situation:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# dpi for the saved figure: https://stackoverflow.com/a/34769840/3129414
dpi = 120
img = mpimg.imread("california_map_blank.png")
height, width, bands = img.shape
# Update figure size based on image size
figsize = width / float(dpi), height / float(dpi)
# Create a figure of the right size with one axes that takes up the full figure
figure = plt.figure(figsize=figsize)
axes = figure.add_axes([0, 0, 1, 1])
# Draw the image
axes.imshow(img, interpolation='nearest')
Here's the result:
First i need to modify the y-axis. I need to inverse it so the 0 start at the bottom. Then I need to modify the value of the axis, [31,42] for y-axis and [-123,-114] for x-axis. Because the point I want to place in this map are all in this range. One example of coordinate: 41.76440000093729, -124.1998.
Now here's my question. Is it possible to achieve this ? How ?
PS: I use python 3.6, and I already know how to place point on the image. I don't need to save the image just showing.
PPS: My final goal in fact is to convert lat/lon data into coordinate in a picture so if you know any other way to do it(in Python of course) please tell me.
EDIT: If I apply this: axes.set_xlim(-124.5,-114) it give me this:
I want to have the axis with this range but with the whole image.
In fact, at the end I will not display the axis I will just put the map with the points, but I need to place the point on the map so I think I need to go through this step.
EDIT2: I tried this: axes.imshow(img[::-1], origin='lower', interpolation='nearest') it works fine to reverse the axis but when I draw a point python draw it in the same place when I the axis was normal.
You need to set the limits of the image via the extent= parameter of imshow. These should be quite precise values for the longitudes left and right, and for the latitudes of bottom and top.
Depending on how deformed the map is, the result can be good enough or not. Try to find the exact longitudes and latitudes of the corners of your map, e.g. via Google Maps.
Depending on how you're running your Python program, matplotlib will show an interactive plot. You can zoom to every region, and the axes will adapt. In the bar at the bottom the x and y-positions will be shown. If they are not the desired ones, you can try to change the extents until they match.
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread("california_map_blank.png")
dpi = 120
height, width, bands = img.shape
# Update figure size based on image size
figsize = width / float(dpi), height / float(dpi)
# Create a figure of the right size with one axes that takes up the full figure
fig, ax = plt.subplots(figsize=figsize)
# find the extent
longitude_top_left = -124.5
longitude_top_right = -113
latitude_bottom_left = 32
latitude_top_left = 42
extent = [longitude_top_left, longitude_top_right, latitude_bottom_left, latitude_top_left]
# Draw the image
ax.imshow(img, interpolation='nearest', extent=extent)
plt.show()
Im trying to scatter a single (square) marker such that it fills the whole figure (no more, no less).
As for simplification, I'm creating a figure such that x- and y- axes both go from -0.5 to 0.5. That is, the plotting area is the unit square, centred at the origin.
The marker now shall be scattered at the origin. What size should it be so that it occupies exactly the unit square?
I looked at this Finding the right marker size for a scatter plot and this pyplot scatter plot marker size but couldn't get it right so far.
This is what I tried:
fig, ax = plt.subplots(figsize=(4,4));
ax.set_aspect('equal');
ax.set_xlim(-0.5, 0.5);
ax.set_ylim(-0.5, 0.5);
figsize = fig.get_size_inches()[0]
dpi = fig.dpi
print(f'figsize = {int(figsize)}')
print(f'dpi = {int(dpi)}')
print(f'figure is {int(figsize*dpi)} x {int(figsize*dpi)} pixels\n')
print(f'setting the marker size to be {int(figsize*dpi)}**2 = {int((figsize*dpi)**2)}')
ax.scatter(0, 0, s=(figsize*dpi)**2, marker='s');
It turns out that the marker (blue area) does fill the unit square but it is actually filling way more than that. After manually trying different sizes, the right value seems to be around 46000 (opposed to the 82944 suggested at the second post).
You will need to apply the aspect, then get the axes width and transform it to display space (or transform the axes position first, then get its width). This can be used to calculate the width of the axes in units of points.
The square of that number is the markersize of the scatter if it shall be as large as the axes.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(4,4))
ax.set_xlim(-0.5, 0.5)
ax.set_ylim(-0.5, 0.5)
ax.set_aspect('equal')
ax.apply_aspect()
s = ax.get_position().transformed(fig.transFigure).width*72/fig.dpi
ax.scatter(0, 0, s=s**2, marker='s');
plt.show()
The objective is to insert a sub_figure in a simple plot as follows:
import numpy as np
from matplotlib import pyplot as plt
X = np.linspace(-6, 6, 1024)
Y = np.sinc(X)
X_detail = np.linspace(-3, 3, 1024)
Y_detail = np.sinc(X_detail)
plt.plot(X, Y, c = 'k')
sub_axes = plt.axes([0.6,0.6,0.25,0.25])
sub_axes.plot(X_detail, Y_detail, c = 'k')
plt.setp(sub_axes)
plt.show()
The code above gives the following output:
The matplotlib documentation says the argument the matplotlib.pyplot.axes() function takes is a list defined as rect=[left, bottom, width, height] where the coordinates left, bottom, width, height are added as normalized (0,1) values.
Can anyone explain that to me ?
The last two co-ordinates are for the size of the sub_figure, that much I get, now what is the deal with the first two ?
The confusion appears to be coming from the different coordinate systems that matplotlib uses. Here is a link to the (fairly exhaustive) tutorial on the subject: https://matplotlib.org/users/transforms_tutorial.html. I will summarize the key point that affect you directly here.
The coordinates you see on your axes are called the data space or data coordinates. This is basically the xlim and ylim of the plots. Note that these are totally independent for the two plots and are not affected by the size or position of your figure.
When you say sub_axes = plt.axes([0.6,0.6,0.25,0.25]), you are specifying the coordinates in figure space or figure coordinates. This is very similar conceptually to axis space or axis coordinates, except that it applies to the whole figure rather than just an individual set of axes.
In this case, the origin of your sub-axes is at (0.6, 0.6) relative to the bottom left corner of the figure. Where the upper-right corner of the figure is (1, 1). As expected, the sub-axes start just a bit above and to the right of the middle of the figure window.
Similarly, the width is (0.25, 0.25), meaning that the sub-axes are 1/4 the size of your figure in each dimension. This can also be interpreted to mean that the upper right-hand corner of the sub-axes is at (0.85, 0.85) in figure space, which looks about right.
You can do some tests. No matter how you pan or zoom on the main axes, the sub-axes are not affected. However, if you resize your figure, both sets of axes will change size to compensate. The sub-axes should always have the same aspect ratio as the figure itself because of how you sized them.
I have a problem of plotting points over a image using matplotlib.pyplot.
As we know, the convention of imshow is that the origin is located on the top left corner, x-axis pointing downward and y-axis pointing rightward.
But when I use plt.plot() to plot some points, the axes seem to becomes x-axis pointing rightward and y-axis pointing downward.
Here is an example. The location of the cursor shown in the windows is x=434 and y=162. However, from the convention of imshow, it should be x=162 and y=434.
Is there a way to ask plot function to obey the convention of imshow, or just let imshow to put the origin at lower left to follow the convention of plot. Thank you very much!
plt.imshow has an option called origin, which changes where the origin is placed. From the docs:
origin : [‘upper’ | ‘lower’], optional, default: None
Place the [0,0] index of the array in the upper left or lower left
corner of the axes. If None, default to rc image.origin.
It would seem form your description, you want to set origin = 'lower'
To switch the x and y axes around, you will also need to transpose your image array. Consider this example:
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
image_file = cbook.get_sample_data('ada.png')
img = plt.imread(image_file)
fig,((ax1,ax2),(ax3,ax4)) = plt.subplots(2,2)
ax1.imshow(img, origin='upper')
ax2.imshow(img, origin='lower')
ax3.imshow(img.transpose(1,0,2), origin='upper')
ax4.imshow(img.transpose(1,0,2), origin='lower')
ax3.set_xlabel('upper')
ax4.set_xlabel('lower')
ax1.set_ylabel('Not transposed')
ax3.set_ylabel('Transposed')
plt.show()
I think you want the lower right axes, so ax4.imshow(img.transpose(1,0,2), origin='lower'). Note that to transpose the image array, we must keep the RGBA channel as the last axes, hence the (1,0,2), to transpose just the first and second axes.