New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't infer x/y coordinates interval breaks for cartopy plot axes #781

Closed
jhamman opened this Issue Mar 3, 2016 · 9 comments

Comments

Projects
None yet
5 participants
@jhamman
Copy link
Member

jhamman commented Mar 3, 2016

The DataArray.plot.pcolormesh() method modifies the x/y coordinates of its plots. I'm finding that, at least for custom cartopy projections, the offset applied here causes some real issues downstream.

@clarkfitzg - Do you see any problem with treating the x/y offset in the same way as the axis limits?

@jhamman jhamman added the plotting label Mar 3, 2016

@shoyer

This comment has been minimized.

Copy link
Member

shoyer commented Mar 3, 2016

Inferring x/y coordinate breaks solves the puzzle of losing the last row and column of data when plotting with pcolormesh.

Are your coordinates very unevenly spaced? I'm curious what goes wrong here -- it's nice to plot all the data if possible.

@jhamman

This comment has been minimized.

Copy link
Member

jhamman commented Mar 3, 2016

@shoyer - I'm not sure that problem exists when plotting with carotpy. I'd have to check though.

Yes, my coordinates are quite unevenly spaced (Equal area projection over the north pole).

My suggestion is that we let cartopy handle locating the coordinates when applicable.

@clarkfitzg

This comment has been minimized.

Copy link
Member

clarkfitzg commented Mar 3, 2016

@jhamman Seems like that would work. Might write a test to make sure the edges don't get dropped. I'd say let cartopy do as much as possible! cartopy integration was a lower priority when I was working on this, but only because we wanted to get the base stuff working first. More seamless integration or documentation for how to use with cartopy would be a good thing.

@jhamman

This comment has been minimized.

Copy link
Member

jhamman commented Mar 3, 2016

A little example of where I'm seeing this go wrong (I think). Here I'm plotting the terrain height variable across the Arctic region, the north pole is somewhere in the middle of the plot. As you can see, there is an odd streak across the plot when using the xarray pcolormesh.

import cartopy.crs as ccrs

# projection class
class Rasm(ccrs.Projection):

    def __init__(self):

        proj4_params = {'R': 6371200.0,
                        'lat_0': 90.0,
                        'lat_1': 90,
                        'lat_2': 90,
                        'lon_0': -114.0+360,
                        'proj': 'lcc',
                        'units': 'm',
                        'x_0': 9469302.950316086,
                        'y_0': 6201952.603370549}

        super(Rasm, self).__init__(proj4_params)

    @property
    def boundary(self):
        coords = ((self.x_limits[0], self.y_limits[0]),(self.x_limits[1], self.y_limits[0]),
                  (self.x_limits[1], self.y_limits[1]),(self.x_limits[0], self.y_limits[1]),
                  (self.x_limits[0], self.y_limits[0]))

        return ccrs.sgeom.Polygon(coords).exterior

    @property
    def threshold(self):
        return 100000.0

    @property
    def x_limits(self):
        return (0, 16423961.103252266)

    @property
    def y_limits(self):
        return (0, 12228062.194885937)

plt.figure()
ax = plt.subplot(projection=Rasm())
elev.plot.pcolormesh('longitude', 'latitude', transform=ccrs.PlateCarree(),
                     vmin=0, vmax=2500, cmap='terrain', add_colorbar=False)
ax.set_title('Using xarray plotting')

plt.figure()
ax = plt.subplot(projection=Rasm())
ax.pcolormesh(elev.longitude, elev.latitude, elev.to_masked_array(),
              transform=ccrs.PlateCarree(), cmap='terrain', vmin=0, vmax=2500)
ax.set_title('Using pyplot and Cartopy')

xarray
cartopy

@shoyer

This comment has been minimized.

Copy link
Member

shoyer commented Mar 3, 2016

OK, that seem pretty compelling to me. You do lose one row of data across the top of the image, but that's better than the artifact.

@mathause

This comment has been minimized.

Copy link
Contributor

mathause commented Aug 19, 2016

Sorry to for joining so late but I am unhappy with this change - because now all the global cartopy maps look like this:

map_no_interval_breaks

and more importantly - there is no way to change this. Thus, I would suggest to add a keyword argument to plot2d.

either

infer_interval_breaks : bool, optional
    Only applies to pcolormesh. If True calculates the edges of the coordinates and if
     False uses the given coordinates. Default True.

or if we don't want to break the current behaviour:

infer_interval_breaks : bool or None, optional
    Only applies to pcolormesh. For None infer_interval_breaks is set to True if the
    axis has no projection and else to False. If True calculates the edges of the coordinates, if
    False uses the given coordinates. Default None.

What is your opinion on this?

@pheidippides

@shoyer shoyer reopened this Aug 19, 2016

@fmaussion

This comment has been minimized.

Copy link
Member

fmaussion commented Oct 28, 2016

Yes, this is definitely a problem.

See for example the differences between imshow (which does fine) and pcolormesh (which is clearly wrong)

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import xarray as xr

da = xr.DataArray(np.arange(20).reshape(4, 5), dims=['lat', 'lon'],
                  coords = {'lat': np.linspace(0, 30, 4),
                            'lon': np.linspace(-20, 20, 5)})

f = plt.figure()
ax = plt.axes(projection=ccrs.PlateCarree())
da.plot.pcolormesh(ax=ax, transform=ccrs.PlateCarree())
lon, lat = np.meshgrid(da.lon.values, da.lat.values)
ax.scatter(lon, lat, transform=ccrs.PlateCarree())
ax.coastlines()
ax.set_title('pcolormesh')

f = plt.figure()
ax = plt.axes(projection=ccrs.PlateCarree())
da.plot.imshow(ax=ax, transform=ccrs.PlateCarree())
ax.scatter(lon, lat, transform=ccrs.PlateCarree())
ax.coastlines()
ax.set_title('imshow')

plt.show()

pcolormesh

imshow

@jhamman

This comment has been minimized.

Copy link
Member

jhamman commented Oct 28, 2016

What got us in trouble before was the approach we took to infering the interval breaks. If you're willing to work through the logic related to 1d and 2d non-uniform coordinates, we can address this.

@shoyer

This comment has been minimized.

Copy link
Member

shoyer commented Oct 28, 2016

I think a keyword argument is a pretty solid way to handle this. Detecting uniform coordinates specified with floating point numbers is pretty error prone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment