简体   繁体   English

Python Matplotlib底图在地图上覆盖小图像

[英]Python Matplotlib Basemap overlay small image on map plot

I am plotting data from an aircraft on a map and I would like to insert this 75px by 29px PNG image of an airplane at the coordinates of the latest data point on the plot. 我正在绘制地图上的飞机数据,我想在绘图上最新数据点的坐标处插入这个75px乘29px的飞机PNG图像。

飞机

As far as I know and have read, pyplot.imshow() is the best way to accomplish this. 据我所知并已阅读, pyplot.imshow()是实现此目的的最佳方法。 However, I am getting hung up on step 1, getting the image to even display. 但是,我在第1步被挂起,让图像均匀显示。 Using a normal plot instead of Basemap, it is easy enough to get the image to appear using imshow, but when using Basemap, I can't get it to show up at all. 使用普通的图而不是Basemap,使用imshow很容易让图像显示,但是当使用Basemap时,我根本无法显示它。 See the example code. 请参阅示例代码。

If I can get the image to display on the map, I am assuming that I can, by trial and error, set its position and some proper dimensions for it using the extent attribute of imshow() , with the plot coordinates converted from the map coordinates x,y = m(lons,lats) . 如果我可以让图像显示在地图上,我假设我可以通过反复试验,使用imshow()extent属性设置它的位置和一些适当的尺寸,从地图转换的绘图坐标坐标x,y = m(lons,lats)

Here is the example code (to try it you may want to download the airplane image above). 这是示例代码(尝试它可能需要下载上面的飞机图像)。

from matplotlib import pyplot as plt
from mpl_toolkits.basemap import Basemap
import Image
from numpy import arange

lats = arange(26,29,0.5)
lons = arange(-90,-87,0.5)

m = Basemap(projection='cyl',llcrnrlon=min(lons)-2,llcrnrlat=min(lats)-2,
            urcrnrlon=max(lons)+2,urcrnrlat=max(lats)+2,resolution='i')

x,y = m(lons,lats)
u,v, = arange(0,51,10),arange(0,51,10)
barbs = m.barbs(x,y,u,v)
m.drawcoastlines(); m.drawcountries(); m.drawstates()

img = Image.open('c130j_75px.png')
im = plt.imshow(img, extent=(x[-1],x[-1]+50000,y[-1],y[-1]+50000))
plt.show()

Here's the resulting image, which doesn't contain a trace of the airplane. 这是生成的图像,不包含飞机的痕迹。 I have tried several different sizes using extent , thinking I may have just made it too small, but with no success. 我已经尝试使用几个不同大小extent ,想我可能只是做它太小了,但没有成功。 I also tried setting zorder=10 , but also with no luck. 我也试过设置zorder=10 ,但也没有运气。 Any help would be appreciated. 任何帮助,将不胜感激。

结果

Update: I can now get the image to at least appear by using m.imshow instead of plt.imshow , since the former passes in the map's axes instance, but the extent argument seems to have no effect on the dimensions of the image, as it always fills up the entire plot no matter how small I make extent dimensions, even if I set them to zero. 更新:我现在可以得到的图像通过使用至少出现m.imshow代替plt.imshow ,因为地图的坐标轴例如前传球,但extent说法似乎对图像的尺寸没有影响,因为它总是填满整个绘图,无论我的extent尺寸有多小,即使我将它们设置为零。 How can I scale the airplane image appropriately and position it near the last data point? 如何适当缩放飞机图像并将其定位在最后一个数据点附近?

im = m.imshow(img, extent=(x[-1],x[-1]+5,y[-1],y[-1]+2))

RESULT2

Actually, for this you want to use a somewhat undocumented feature of matplotlib: the matplotlib.offsetbox module. 实际上,为此您需要使用matplotlib的一些未记录的功能: matplotlib.offsetbox模块。 There's an example here: http://matplotlib.sourceforge.net/trunk-docs/examples/pylab_examples/demo_annotation_box.html 这里有一个例子: http//matplotlib.sourceforge.net/trunk-docs/examples/pylab_examples/demo_annotation_box.html

In your case, you'd do something like this: 在你的情况下,你会做这样的事情:

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

from mpl_toolkits.basemap import Basemap
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

# Set up the basemap and plot the markers.
lats = np.arange(26, 29, 0.5)
lons = np.arange(-90, -87, 0.5)

m = Basemap(projection='cyl',
            llcrnrlon=min(lons) - 2, llcrnrlat=min(lats) - 2,
            urcrnrlon=max(lons) + 2, urcrnrlat=max(lats) + 2,
            resolution='i')

x,y = m(lons,lats)
u,v, = np.arange(0,51,10), np.arange(0,51,10)
barbs = m.barbs(x,y,u,v)

m.drawcoastlines()
m.drawcountries()
m.drawstates()

# Add the plane marker at the last point.
plane = np.array(Image.open('plane.jpg'))
im = OffsetImage(plane, zoom=1)
ab = AnnotationBbox(im, (x[-1],y[-1]), xycoords='data', frameon=False)

# Get the axes object from the basemap and add the AnnotationBbox artist
m._check_ax().add_artist(ab)

plt.show()

在此输入图像描述

The advantage to this is that the plane is in axes coordinates and will stay the same size relative to the size of the figure when zooming in. 这样做的优点是平面处于轴坐标,并且在放大时将保持相对于图形尺寸的相同尺寸。

With basemap, you can generally just use normal pyplot style commands if you translate your coordinates using the map instance first. 使用底图,如果首先使用地图实例转换坐标,通常只需使用普通的pyplot样式命令。 In this case, you can just transform the extent into uv coordinates with: 在这种情况下,您只需将范围转换为uv坐标:

x0, y0 = m(x[-1], y[-1])
x1, y1 = m(x[-1] + 0.5, y[-1] + 0.5)

And then subsequently you will be able to do: 然后你将能够做到:

im = plt.imshow(img, extent=(x0, x1, y0, y1))

My full solution to this looks like: 我对此的完整解决方案如下:

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np


lats = np.arange(26, 29, 0.5)
lons = np.arange(-90, -87, 0.5)

m = Basemap(projection='cyl', llcrnrlon=min(lons)-2, llcrnrlat=min(lats)-2,
            urcrnrlon=max(lons)+2, urcrnrlat=max(lats)+2, resolution='h')

x, y = m(lons,lats)
u, v = np.arange(0, 51, 10), np.arange(0, 51, 10)
barbs = m.barbs(x, y, u, v)

m.drawcoastlines()
m.fillcontinents()

x_size, y_size = 0.8, 0.4
x0, y0 = m(x[-1] - x_size/2., y[-1] - y_size/2.)
x1, y1 = m(x[-1] + x_size/2., y[-1] + y_size/2.)
im = plt.imshow(plt.imread('mslr86.png'), extent=(x0, x1, y0, y1))

plt.show()

Which produces an image that looks like 这会产生一个看起来像的图像 在此输入图像描述

Update: if you want the image to remain a fixed size, independent of the zoom, see Joe's answer. 更新:如果您希望图像保持固定大小,与缩放无关,请参阅Joe的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM