matplotlib / basemap

Plot on map projections (with coastlines and political boundaries) using matplotlib
MIT License
780 stars 392 forks source link

zorder of drawmapboundary() gets on top of imshow() #474

Open Xunius opened 5 years ago

Xunius commented 5 years ago

Hi all,

I got this weird issue that changing the order of drawmapboundary() wrt drawparallels() and drawmeridians() gives different results. Specifically, calling drawmapboundary(fill_color='0.8') first leads to an overlay of grey over the imshow() (see left panel in figure). While putting drawmapboundary() after drawparallels() and drawmeridians() gives correct results (right panel in figure).

basemap

The script to create that plot:

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

def plotBmap(data, x, y, ax, order):

    bmap=Basemap(projection='cyl',\
            llcrnrlat=y[0],llcrnrlon=x[0],\
            urcrnrlat=y[-1],urcrnrlon=x[-1],\
            ax=ax)

    bmap.imshow(data, interpolation='nearest')

    # plot axes labels
    lat_labels=np.linspace(-90, 90, 5)
    lon_labels=np.linspace(0, 360, 6)
    ax.set_yticks(lat_labels)
    ax.set_xticks(lon_labels)
    ax.tick_params(axis='both',which='major',labelsize=10)
    ax.xaxis.set_ticklabels([])
    ax.yaxis.set_ticklabels([])

    # draw continents
    bmap.drawcoastlines(linewidth=1.5,linestyle='solid',color='k',\
        antialiased=True)
    bmap.drawcountries(linewidth=0.5,linestyle='solid',color='k',\
            antialiased=True)

    if order=='before':
        bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8')
        bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
                labelstyle='+/-',fontsize=10)
    elif order=='after':
        bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8')

    return bmap

if __name__=='__main__':

    x=np.arange(0, 361, 1)     # lon
    y=np.arange(-90, 91, 1)    # lat
    X,Y=np.meshgrid(x,y)
    z=np.sin(X/90.)+np.cos(Y/90.)

    # create an area of missings
    z[10:50, 20:80]=np.nan

    fig, (ax1, ax2)=plt.subplots(1, 2, figsize=(12,6))

    bmap1=plotBmap(z, x, y, ax1, 'before')
    ax1.set_title('before')

    bmap2=plotBmap(z, x, y, ax2, 'after')
    ax2.set_title('after')

    fig.show()

I guess it's the zorder that is wrong. specifying zorder=0 in drawmapboundary() doesn't work, by zordre=-1 does.

The versions of relevant packages:

One of the combinations that doesn't have this issue:

WeatherGod commented 5 years ago

Fascinating...

I still need to dig into exactly what the code looked like back in v1.0.7, but one thing I am suspecting is that between then and now, I normalized the limb-handling code across all of the drawing functions. In particular, the drawparallels() and drawmeridians() methods would trigger a call to _cliplimb() now, which exits early if the map boundaries haven't been drawn yet. But, I don't know how setting clip paths could possibly have an impact on any of this.

Now, one thing about this is that drawmapboundary() defaults to zorder of 0, so explicitly setting a zorder of zero would have no impact on behavior.

I am going to have to investigate this a bit further to figure out the cause of the issue.

On Wed, Aug 21, 2019 at 6:22 AM Xunius notifications@github.com wrote:

Hi all,

I got this weird issue that changing the order of drawmapboundary() wrt drawparallels() and drawmeridians() gives different results. Specifically, calling drawmapboundary(fill_color='0.8') first leads to an overlay of grey over the imshow() (see left panel in figure). While putting drawmapboundary() after drawparallels() and drawmeridians() gives correct results (right panel in figure).

[image: basemap] https://user-images.githubusercontent.com/8076931/63423894-d2caca80-c43f-11e9-9544-a224c703c007.png

The script to create that plot:

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

def plotBmap(data, x, y, ax, order):

bmap=Basemap(projection='cyl',\
        llcrnrlat=y[0],llcrnrlon=x[0],\
        urcrnrlat=y[-1],urcrnrlon=x[-1],\
        ax=ax)

bmap.imshow(data, interpolation='nearest')

# plot axes labels
lat_labels=np.linspace(-90, 90, 5)
lon_labels=np.linspace(0, 360, 6)
ax.set_yticks(lat_labels)
ax.set_xticks(lon_labels)
ax.tick_params(axis='both',which='major',labelsize=10)
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])

# draw continents
bmap.drawcoastlines(linewidth=1.5,linestyle='solid',color='k',\
    antialiased=True)
bmap.drawcountries(linewidth=0.5,linestyle='solid',color='k',\
        antialiased=True)

if order=='before':
    bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8')
    bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
            labelstyle='+/-',fontsize=10)
    bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
            labelstyle='+/-',fontsize=10)
elif order=='after':
    bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
            labelstyle='+/-',fontsize=10)
    bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
            labelstyle='+/-',fontsize=10)
    bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8')

return bmap

if name=='main':

x=np.arange(0, 361, 1)     # lon
y=np.arange(-90, 91, 1)    # lat
X,Y=np.meshgrid(x,y)
z=np.sin(X/90.)+np.cos(Y/90.)

# create an area of missings
z[10:50, 20:80]=np.nan

fig, (ax1, ax2)=plt.subplots(1, 2, figsize=(12,6))

bmap1=plotBmap(z, x, y, ax1, 'before')
ax1.set_title('before')

bmap2=plotBmap(z, x, y, ax2, 'after')
ax2.set_title('after')

fig.show()

I guess it's the zorder that is wrong. specifying zorder=0 in drawmapboundary() doesn't work, by zordre=-1 does.

The versions of relevant packages:

  • basemap = 1.2.0
  • matplotlib = 2.2.3

One of the combinations that doesn't have this issue:

  • basemap = 1.0.7
  • matplotlib = 2.2.3

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/matplotlib/basemap/issues/474?email_source=notifications&email_token=AACHF6CJSS2VSUZ5ONADI23QFUJMRA5CNFSM4IOE3SP2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HGPC7RQ, or mute the thread https://github.com/notifications/unsubscribe-auth/AACHF6BO7ELAMVNSAQQBU23QFUJMRANCNFSM4IOE3SPQ .

Xunius commented 4 years ago

Hi, I wonder is there any fix being patched, but I noticed something new.

I created a new conda environment and installed basemap = 1.2.0 and matplotlib = 2.2.3. Now setting zorder=-1 in drawmapboundary() makes the top and the left hand side black frames gone (see top-right in figure below), as if the fill_color is overlaying the figure frame. Again it seems to be related to the ordering of drawmapboundary() wrt drawparallels() and drawmeridians(). I've created a new plot to show this:

basemap2

Code below:

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

def plotBmap(data, x, y, ax, method):

    bmap=Basemap(projection='cyl',\
            llcrnrlat=y[0],llcrnrlon=x[0],\
            urcrnrlat=y[-1],urcrnrlon=x[-1],\
            ax=ax)

    bmap.imshow(data, interpolation='nearest')

    # plot axes labels
    lat_labels=np.linspace(-90, 90, 5)
    lon_labels=np.linspace(0, 360, 6)
    ax.set_yticks(lat_labels)
    ax.set_xticks(lon_labels)
    ax.tick_params(axis='both',which='major',labelsize=10)
    ax.xaxis.set_ticklabels([])
    ax.yaxis.set_ticklabels([])

    # draw continents
    bmap.drawcoastlines(linewidth=1.5,linestyle='solid',color='k',\
        antialiased=True)
    bmap.drawcountries(linewidth=0.5,linestyle='solid',color='k',\
            antialiased=True)

    if method=='before no zorder':
        bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8')
        bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
                labelstyle='+/-',fontsize=10)
    elif method=='before with zorder':
        bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8', zorder=-1)
        bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
                labelstyle='+/-',fontsize=10)
    elif method=='after no zorder':
        bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8')
    elif method=='after with zorder':
        bmap.drawparallels(lat_labels,labels=[1,1,0,0],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmeridians(lon_labels,labels=[0,0,0,1],linewidth=0,\
                labelstyle='+/-',fontsize=10)
        bmap.drawmapboundary(color='k',linewidth=1.0,fill_color='0.8', zorder=-1)

    ax.set_title(method)

    return bmap

if __name__=='__main__':

    x=np.arange(0, 361, 1)     # lon
    y=np.arange(-90, 91, 1)    # lat
    X,Y=np.meshgrid(x,y)
    z=np.sin(X/90.)+np.cos(Y/90.)

    # create an area of missings
    z[10:50, 20:80]=np.nan

    fig, axes=plt.subplots(2, 2, figsize=(12,6))

    bmap1=plotBmap(z, x, y, axes[0,0], 'before no zorder')
    bmap1=plotBmap(z, x, y, axes[0,1], 'before with zorder')
    bmap1=plotBmap(z, x, y, axes[1,0], 'after no zorder')
    bmap1=plotBmap(z, x, y, axes[1,1], 'after with zorder')

    fig.show()