問題提起
表題の通り.大きめの白地図に小さめの2次元マップを描画したいのに...という状態.
使用言語はpython3.7.2系でも同様の問題があるかは未確認.
説明
広めの日本地図(緯度経度範囲:(120, 150, 30, 50))に,狭めの2次元配列(緯度経度範囲:(130, 140, 35, 45))を貼り付けることを考える.
まずは日本地図を描く.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
# coords
basemap_extent = (120, 150, 30, 50)
array_extent = (130, 140, 35, 45)
# array
ar = np.arange(25).reshape(5,5)
# set figure & axis
fig = plt.figure()
ax = fig.add_subplot(111)
# set up basemap on axis
m = Basemap(ax=ax,
llcrnrlon = basemap_extent[0],
urcrnrlon = basemap_extent[1],
llcrnrlat = basemap_extent[2],
urcrnrlat = basemap_extent[3],
resolution='l')
# draw lines
m.drawcoastlines(linewidth=0.4)
m.drawmeridians(np.arange(basemap_extent[0], basemap_extent[1]+1, 5), linewidth=0.5, dashes=[2,1],labels=[0,0,0,1])
m.drawparallels(np.arange(basemap_extent[2], basemap_extent[3]+1, 5), linewidth=0.5, dashes=[2,1],labels=[1,0,0,0])
plt.show()
問題なし.
次に,何も考えず2次元配列を貼り付ける.
# draw array (default extent)
im = m.imshow(ar)
plt.show()
画面全体に貼り付けられた.まあ想定通り.
さて,extentを使用してみる.下記はimshow()の公式説明からの抜粋.
extent : scalars (left, right, bottom, top), optional
The bounding box in data coordinates that the image will fill. The image is stretched individually along x and y to fill the box.
# draw array (set extent)
im = m.imshow(ar,extent=array_extent)
plt.show()
あれ,変わらない.
extentが反映されているかどうかを確認してみる.
print(im.get_extent()) # -> (120.0, 150.0, 30.0, 50.0)
うん,反映されていない.バグと呼んでいいと思います.
解決策
extentの機能を使いたいときは,下記のようにすればOK.
# draw array (default)
im = m.imshow(ar)
# then, set extent
im.set_extent(array_extent)
plt.show()
これが描きたかった.
おまけ1(失敗例)
小さい2次元配列に合わせてbasemapを設定し,後からaxisを拡張してみたが,うまくいかなかった.軸目盛ラベルまで取り残されるとは...
# set figure & axis
fig = plt.figure()
ax = fig.add_subplot(111)
# set up basemap on axis
# with array_extent
m = Basemap(ax=ax,
llcrnrlon = array_extent[0],
urcrnrlon = array_extent[1],
llcrnrlat = array_extent[2],
urcrnrlat = array_extent[3],
resolution='l')
# draw lines
m.drawcoastlines(linewidth=0.4)
m.drawmeridians(np.arange(basemap_extent[0], basemap_extent[1]+1, 5), linewidth=0.5, dashes=[2,1],labels=[0,0,0,1])
m.drawparallels(np.arange(basemap_extent[2], basemap_extent[3]+1, 5), linewidth=0.5, dashes=[2,1],labels=[1,0,0,0])
# draw array (default)
im = m.imshow(ar)
# expand outer region
ax.set_xlim(basemap_extent[0], basemap_extent[1])
ax.set_ylim(basemap_extent[2], basemap_extent[3])
おまけ2(cartopyによる描画)
こちらはうまく動く.軸の範囲(extent)指定時には,crsを必ず設定する必要がある.ぴっちり設定すると画面周上の軸が消えてしまうのはもったいない.
# coords
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
basemap_extent = (120, 150, 30, 50)
array_extent = (130, 140, 35, 45)
# array
ar = np.arange(25).reshape(5,5)
# set figure & axis (with projection)
fig = plt.figure()
ax = fig.add_subplot(111,projection=ccrs.PlateCarree())
ax.set_extent(basemap_extent,crs=ccrs.PlateCarree())
# draw lines
ax.coastlines('10m',linewidth=0.4)
gl = ax.gridlines(linewidth=0.5, draw_labels=True, linestyle='--')
gl.xlocator = mticker.FixedLocator(np.arange(basemap_extent[0], basemap_extent[1]+1, 5))
gl.ylocator = mticker.FixedLocator(np.arange(basemap_extent[2], basemap_extent[3]+1, 5))
gl.xlabels_top = False
gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
# draw array (set extent)
im = ax.imshow(ar,extent=array_extent)
plt.show()