how to use 'extent' in matplotlib.pyplot.imshow - python

I managed to plot my data and would like to add a background image (map) to it.
Data is plotted by the long/lat values and I have the long/lat values for the image's three corners (top left, top right and bottom left) too.
I am trying to figure out how to use 'extent' option with imshow. However, the examples I found don't explain how to assign x and y for each corner ( in my case I have the information for three corners).
How can I assign the location of three corners for the image when adding it to the plot?
Thanks

Specify, in the coordinates of your current axis, the corners of the rectangle that you want the image to be pasted over
Extent defines the left and right limits, and the bottom and top limits. It takes four values like so: extent=[horizontal_min,horizontal_max,vertical_min,vertical_max].
Assuming you have longitude along the horizontal axis, then use extent=[longitude_top_left,longitude_top_right,latitude_bottom_left,latitude_top_left]. longitude_top_left and longitude_bottom_left should be the same, latitude_top_left and latitude_top_right should be the same, and the values within these pairs are interchangeable.
If your first element of your image should be plotted in the lower left, then use the origin='lower' imshow option as well, otherwise the 'upper' default is what you want.

Here's an example based on http://matplotlib.org/examples/pylab_examples/image_demo3.html showing use of extent.
#!/usr/bin/env python
from pylab import *
try:
from PIL import Image
except ImportError, exc:
raise SystemExit("PIL must be installed to run this example")
import matplotlib.cbook as cbook
datafile = cbook.get_sample_data('ada.png')
h = Image.open(datafile)
dpi = rcParams['figure.dpi']
figsize = h.size[0]/dpi, h.size[1]/dpi
figure(figsize=figsize)
ax = axes([0,0,1,1], frameon=False)
ax.set_axis_off()
ax.set_xlim(0,2)
ax.set_ylim(0,2)
im = imshow(h, origin='upper',extent=[-2,4,-2,4]) # axes zoom in on portion of image
im2 = imshow(h, origin='upper',extent=[0,.5,0,.5]) # image is a small inset on axes
show()
If you don't set your axis limits, they become your extents & then don't seem to have any effect.

Related

Specify axes position in pixels (or inches) in matplotlib

I'm trying to add an Axes to a matplotlib figure (that I will than use to show an image).
At the moment I'm using normalized coordinates, but since the size of my figure could change, I would like to specify the position in pixels or inches to be more robust.
I tried for example:
fig = plt.figure(figsize=(2, 2)) # Creates a 2"x 2" image
ax = fig.add_axes([1, 1, 1, 1], transform="figure-inches") # Creates an axes with origin at (1",1") and size 1"x 1"
I expect the axes to occupy the top-right quarter of my figure. However what I obtain is different, with the axes' origin in the top-right corner and the rest of the axes outside the Figure.
I tried to read the add_axes documentation as well as the Transform tutorial. However I find this part of the documentation a bit confusiong.
How can I create an Axes specifying it's size and posizion in physical units instead of normalized ones?

How to modify some axis' attribute with an image in python?

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

matplotlib.pyplot.axes() arguments confusion

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.

Change range withouth scaling in matplot

I have a question, I am making a program that displays a zoomed area of Peru but the axis shown are in the range of the image (e.g. 7000x7500) but i want to be in UTM range (e.g. x-axis between 500000-600000 and y-axis 9500000-9700000)
I have tried using plt.set_xlim and plt.sety_lim but no success, I think I have to use plt.autoscale(False) but it also didn't work or I used it wrong.
I create the figure and axes out of the main program
f = plt.figure(figsize=(5,5))
axe = f.add_axes([0, 0, 1, 1])
this is the function I call everytime I want to plot
def plotear(self, mapa):
axe.clear()
axe.imshow(mapa, cmap='gray', interpolation='nearest')
axe.set_xlim(0,10000) #This is just for testing
axe.set_ylim(0,10000) #This is just for testing
plt.autoscale(False)
self.canvas.draw()
Edit: #ImportanceOfBeingErnest's answer worked as expected! Now I am having another problem, in the canvas I am showing the image, the x-axis and y-axis doesnt visualize correctly, here is an image example
how could I fix it? thanks.
From the imshow documentation you'd find that there is an argument extent which can be used to scale the image.
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.
In this case you'd use it like
ax.imshow(mapa, extent=[5e5, 6e5, 9.5e6, 9.7e6])
Answer to the edited question:
In the case of the image being too large, this is probably caused by you setting axe = f.add_axes([0, 0, 1, 1]). You should rather use ax = fig.add_subplot(111) and if the margins are not as you want then, setting plt.subplots_adjust( ... ) with the respective spacings.

matplotlib: coordinates convention of image imshow incompatible with plot

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.

Categories

Resources