使用双三次插值的彩色 matplotlib 地图

Posted

技术标签:

【中文标题】使用双三次插值的彩色 matplotlib 地图【英文标题】:color matplotlib map using bicubic interpolation 【发布时间】:2014-08-21 03:35:06 【问题描述】:

我知道 matplotlib 和 scipy 可以做双三次插值: http://matplotlib.org/examples/pylab_examples/image_interp.html http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html http://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp2d.html

我也知道用matplotlib画世界地图是可以的: http://matplotlib.org/basemap/users/geography.html http://matplotlib.org/basemap/users/examples.html http://matplotlib.org/basemap/api/basemap_api.html

但是我可以根据 4 个数据点进行双三次插值并且只对陆地进行着色吗?

例如将这些用于 4 个数据点(经度和纬度)和颜色:

Lagos: 6.453056, 3.395833; red HSV 0 100 100 (or z = 0)
Cairo: 30.05, 31.233333; green HSV 90 100 100 (or z = 90)
Johannesburg: -26.204444, 28.045556; cyan HSV 180 100 100 (or z = 180)
Mogadishu: 2.033333, 45.35; purple HSV 270 100 100 (or z = 270)

我认为必须可以在经纬度范围内进行双三次插值,然后在该图层顶部添加海洋、湖泊和河流?我可以用drawmapboundary 做到这一点。实际上有一个选项maskoceans: http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.maskoceans

我可以像这样插入数据:

xnew, ynew = np.mgrid[-1:1:70j, -1:1:70j]
tck = interpolate.bisplrep(x, y, z, s=0)
znew = interpolate.bisplev(xnew[:,0], ynew[0,:], tck)

或者scipy.interpolate.interp2d: http://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp2d.html

这里解释了如何转换为地图投影坐标: http://matplotlib.org/basemap/users/mapcoords.html

但我需要弄清楚如何为计算表面而不是单个点执行此操作。实际上有一个使用外部数据的地形图示例,我应该能够复制: http://matplotlib.org/basemap/users/examples.html

附:我不是在寻找一个完整的解决方案。我更愿意自己解决这个问题。相反,我正在寻找建议和提示。我使用 gnuplot 已经 10 多年了,并且在过去几周内才切换到 matplotlib,所以请不要以为我知道关于 matplotlib 的最简单的事情。

【问题讨论】:

我自己也想弄清楚同样的事情。运气好吗? 这对我来说将是一个圣诞节项目。所以如果你能等到 2015 年…… 您真的想要一个表面,而不是一组(可能非常密集的)点?您是指 interp2d 输出的函数类型吗? 是的,在这个阶段有一组密集的点就可以了。我似乎找不到时间来做这件事。我应该删除这个问题。为此,我将为自己设定一个截止日期为 4 月 1 日。 看看这个例子:webrian.ch/2012/11/heat-maps-with-python.html 【参考方案1】:

请注意,做相反的事情,即在海面上放置一个栅格并在大陆上放置一个掩膜,这很容易。只需使用map.fillcontinents()。所以这个解决方案的基本思想是修改fillcontinents函数,让它在海洋上放置多边形。

步骤如下:

    创建一个覆盖整个地球的大型圆形多边形。 为map.coastpolygons 数组中的每个形状创建一个多边形。 使用shapely 及其difference 方法将陆地多边形的形状从圆中剪掉。 在顶部添加具有海洋形状的剩余多边形,并带有高 zorder

代码:

from mpl_toolkits.basemap import Basemap
import numpy as np
from scipy import interpolate
from shapely.geometry import Polygon
from descartes.patch import PolygonPatch

def my_circle_polygon( (x0, y0), r, resolution = 50 ):
    circle = []
    for theta in np.linspace(0,2*np.pi, resolution):
        x = r * np.cos(theta) + x0
        y = r * np.sin(theta) + y0    
        circle.append( (x,y) )

    return Polygon( circle[:-1] )

def filloceans(the_map, color='0.8', ax=None):
    # get current axes instance (if none specified).
    if not ax:     
        ax = the_map._check_ax()

    # creates a circle that covers the world
    r =  0.5*(map.xmax - map.xmin) # + 50000 # adds a little bit of margin
    x0 = 0.5*(map.xmax + map.xmin)
    y0 = 0.5*(map.ymax + map.ymin)
    oceans = my_circle_polygon( (x0, y0) , r, resolution = 100 )

    # for each coastline polygon, gouge it out of the circle
    for x,y in the_map.coastpolygons:
        xa = np.array(x,np.float32)
        ya = np.array(y,np.float32)

        xy = np.array(zip(xa.tolist(),ya.tolist()))
        continent = Polygon(xy)

        ##  catches error when difference with lakes 
        try:
            oceans = oceans.difference(continent)
        except: 
            patch = PolygonPatch(continent, color="white", zorder =150)
            ax.add_patch( patch ) 

    for ocean in oceans:
        sea_patch = PolygonPatch(ocean, color="blue", zorder =100)
        ax.add_patch( sea_patch )

###########  DATA
x = [3.395833, 31.233333, 28.045556, 45.35   ]
y = [6.453056, 30.05,    -26.204444, 2.033333]
z = [0, 90, 180, 270]

# set up orthographic map projection
map = Basemap(projection='ortho', lat_0=0, lon_0=20, resolution='l')

## Plot the cities on the map
map.plot(x,y,".", latlon=1)

# create a interpolated mesh and set it on the map
interpol_func = interpolate.interp2d(x, y, z, kind='linear')
newx = np.linspace( min(x), max(x) )
newy = np.linspace( min(y), max(y) )
X,Y = np.meshgrid(newx, newy)
Z = interpol_func(newx, newy)
map.pcolormesh( X, Y, Z, latlon=1, zorder=3)

filloceans(map, color="blue")

瞧:

【讨论】:

此解决方案并非没有问题。首先它很慢,然后地图边缘的小陆地没有正确渲染,最后没有测试鲁棒性,它不适用于其他投影。我会尝试改进这些方面并更新答案,但请随时提出建议。 什么是descartes?我好像没有。 拉各斯在您的地图上似乎是青色而不是红色。 好吧,插值栅格可能与您的不同。我的重点是掩盖海洋。至于双三次插值,我认为 4 个点不足以让您这样做。您将需要更大的集合或线性插值。这能回答你的评论吗?【参考方案2】:

我认为这就是您正在寻找的(大致)。请注意,在绘制pcolor 并传入hsv 颜色图之前,关键的事情是屏蔽数据数组(文档:cmap parameter for pcolormesh 和available colormaps)。

我将用于绘制地图的代码保留在与示例非常接近的地方,因此应该很容易理解。出于同样的原因,我保留了您的插值代码。请注意,插值是线性的而不是三次的 - kx=ky=1 - 因为你没有给出足够的点来进行三次插值(你至少需要 16 - scipy 会抱怨说 "m must be >= (kx+1)(ky+1)" ,尽管documentation 中没有提到约束)。

我还扩展了网格的范围,并将 x 和 y 始终保持在纬度/经度。

代码

from mpl_toolkits.basemap import Basemap,maskoceans
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
# set up orthographic map projection with
# perspective of satellite looking down at 0N, 20W (Africa in main focus)
# use low resolution coastlines.
map = Basemap(projection='ortho',lat_0=0,lon_0=20,resolution='l')

# draw coastlines, country boundaries
map.drawcoastlines(linewidth=0.25)
map.drawcountries(linewidth=0.25)
# Optionally (commented line below) give the map a fill colour - e.g. a blue sea
#map.drawmapboundary(fill_color='aqua')
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0,360,30))
map.drawparallels(np.arange(-90,90,30))


data = 'Lagos': (6.453056, 3.395833,0),
        'Cairo': (30.05, 31.233333,90),
        'Johannesburg': (-26.204444, 28.045556,180),
        'Mogadishu': (2.033333, 45.35, 270)

x,y,z = zip(*data.values())

xnew, ynew = np.mgrid[-30:60:0.1, -50:50:0.1]

tck = interpolate.bisplrep(x, y, z, s=0,kx=1,ky=1)
znew = interpolate.bisplev(xnew[:,0], ynew[0,:], tck)
znew = maskoceans(xnew, ynew, znew)

col_plot = map.pcolormesh(xnew, ynew, znew, latlon=True, cmap='hsv')
plt.show()

输出

【讨论】:

@tommy.carstensen 这是否给了您想要的东西,或者这里缺少什么?如果有什么不适合您的,请随时联系我。 我会给你赏金。如果过期了,我会再发一次。我只想根据您的答案提供一个示例,其中使用双三次插值提供更多数据点。您的示例因 16 个或更多数据点而失败。抱歉耽搁了。我会在这个周末完成它。感谢您的出色回答,我已经投了赞成票。 酷 - 没问题。如果你有 16 个数据点(或者可以告诉我从哪里得到它们),我很高兴用那个数据集来看看它。我无法弄清楚这些数据的来源,但我不是非洲城市的专家。即使您授予此赏金,我也会重新考虑 - 这是一个有趣的问题,我不希望您浪费更多的代表。

以上是关于使用双三次插值的彩色 matplotlib 地图的主要内容,如果未能解决你的问题,请参考以下文章

使用双三次插值填充矩阵的边界

双三次插值

java 缩放算法 双线性插值,双三次插值

数字图像缩放之双三次插值

您如何对重新采样的音频数据进行双三次(或其他非线性)插值?

OpenCV图像缩放插值之BiCubic双三次插值