Repository: SciTools/cartopy

Issue:
Imshow keyword arg 'origin' issue
When plotting an image on a set of Cartopy geoaxes with a native projection not equal to the image's projection, an issue with the origin keyword argument becomes apparent. This kwarg has two options; 'upper' and 'lower', where 'upper' should be appropriate for images (such as jpegs or geotiffs) that define an origin in the upper-left corner of the image.

It can be shown that this kwarg is not being interpreted correctly by using an example from the Cartopy docs: http://scitools.org.uk/cartopy/docs/latest/matplotlib/advanced_plotting.html#images with the following minor changes:

``` python
import os
import matplotlib.pyplot as plt

from cartopy import config
import cartopy.crs as ccrs

img_proj = ccrs.Miller()
plot_proj = ccrs.PlateCarree()

fig = plt.figure(figsize=(8, 12))

# get the path of the file. It can be found in the repo data directory.
fname = os.path.join(config["repo_data_dir"],
                     'raster', 'sample', 'Miriam.A2012270.2050.2km.jpg'
                     )
img_extent = (-120.67660000000001, -106.32104523100001,
              13.2301484511245, 30.766899999999502)
img = plt.imread(fname)[::-1, ...]

ax = plt.axes(projection=plot_proj)
ax.set_global()
plt.title('Hurricane Miriam from the Aqua/MODIS satellite\n'
          '2012 09/26/2012 20:50 UTC')

# set a margin around the data
ax.set_xmargin(0.05)
ax.set_ymargin(0.10)

# add the image. Because this image was a tif, the "origin" of the image is
# in the upper left corner.
ax.imshow(img, origin='lower', transform=img_proj, extent=img_extent)
ax.coastlines(resolution='50m', color='black', linewidth=1)

plt.show()
```

The changes made here are altering the axes projection to Miller and setting the axes extent to global, the combination of which makes the issue evident.

To demonstrate the issue, removing the array manipulation when `img` is made and changing the origin kwarg's value to `upper` moves the position of the image from the correct location (generated by the code above), to a location of appropriate longitude but incorrect latitude. This may be seen much more clearly by comparing the two images found below, where `origin='upper'` is set in the top image, but `origin='lower'` (with appropriate array manipulation) is set in the lower.

![imshow_origin_bug_upper](https://f.cloud.github.com/assets/3473068/1307609/60558118-31c2-11e3-885a-13d0c4222459.png)
![imshow_origin_bug_lower](https://f.cloud.github.com/assets/3473068/1307610/605a60ac-31c2-11e3-9a8e-f0ed01c991f2.png)

Vector handling
Consider the ability to plot vector fields in Cartopy. The initial usecase should:
- Take data on a rotated pole grid (for example Met Office [NAE output](http://www.metoffice.gov.uk/research/modelling-systems/unified-model/weather-forecasting)) of wind data (u and v component) which is co-located and where the u and v component are in the direction of the grid rather than east/north.
- Plot the data on its native grid (no transformation necessary)
- Plot the data on a PlateCarree map (the vector magnitude is preserved, but its direction needs to be transformed).
- Plot the data on an arbitrary map (may be no more difficult than the PlateCarree case)

**To others**: Please feel free to update this issue with clarifications/questions.



Discussion:
This must be related to #358, if not the same bug. It sure looks similar.

OK I've nailed down the weird origin behaviour. When the image is interpolated so that it can be displayed on the new projection there is an implicit assumption that the image array is structured from lower left to upper right, meaning that the regridded image is wrong if it is in fact upper left to lower right. Adding this after [L744 in geoaxes.py](https://github.com/SciTools/cartopy/blob/master/lib/cartopy/mpl/geoaxes.py#L744) gives the expected behaviour:

``` python
if kwargs['origin'] == 'upper':
    # we implicitly assume the orientation is lower when we regrid
    img = img[::-1]
    kwargs['origin'] = 'lower'
```

However, this does not solve the artefacts outside of the expected domain... I shall see if I can locate that problem before making a PR as they may well be related issues.

OK I think I have this one sorted, the artefacts at the edges of the plot are due to the nearest neighbour interpolation routine not masking off points outside the original image. I'll try and put a PR together soon and update here.

Nice one :+1: 

Thanks for the comprehensive bug report @dkillick.

This feature will be pretty important to have in order for Cartopy to become more widely used. I was just wondering if work has begun on this? 

I'm not aware of any work on this yet, but @pelson might have something cooking in his basement... ?

Hi @ajdawson - no, I've not got anything done on this yet. Plotting the data in native _should_ just work, though I've not tried it - the real difficulty is in handling the projected vectors. Do you have any experience in this field? (Given your work on windspharm, I'm guessing you do). If so, I'd love to hear some ideas on what you think needs implementing, if your keen?

Cheers,

@pelson I actually don't have much experience with this.

Indeed it does seem to work for the native projection. In an ideal world it would be possible to take a vector field defined on any projection and plot it on any other projections. For instance, I'd like to plot vector fields defined on a PlateCarree map in a polar stereographic projection.

As for implementing this... I've never looked deeply into how projections for scalar data are handled in cartopy, so I'm not sure where to start!

> Take data on a rotated pole grid (for example Met Office NAE output) of wind data (u and v component) which is co-located and where the u and v component are in the direction of the grid rather than east/north.

@pelson - I've got a prototype of this working. My bare bones implementation will transform from PlateCarree to other projections (I've tested NorthPolarStereo and Robinson so far) but I'm thinking round in circles about whether it should work for a rotated pole grid as you describe. Can you point me in the direction of any data like this?

I guess you could try the data at: http://scitools.org.uk/iris/docs/latest/examples/graphics/rotated_pole_mapping.html#rotated-pole-mapping-03

I could get hold of some vector data on this projection (actually, it would be a slightly different domain, but in principle the same) if that would be helpful.

I guess I could invent some data on that grid to test things out. I can think about real data if I get it working.

> I can think about real data if I get it working.

If you remind me on Monday, I'll get hold of some real data for you (it'll have to be by email for now though).

OK thanks @pelson.

OK @pelson, I made some dummy data to test with rotated pole grid and things look good. If you could get hold of some real data for me it would be very useful. Email is fine.


Resolving patch:
diff --git a/lib/cartopy/examples/arrows.py b/lib/cartopy/examples/arrows.py
new file mode 100644
--- /dev/null
+++ b/lib/cartopy/examples/arrows.py
@@ -0,0 +1,40 @@
+import matplotlib.pyplot as plt
+import numpy as np
+
+import cartopy.crs as ccrs
+
+
+def gen_data():
+    # Generate some data
+    x = np.arange(-60, 45, 5)
+    y = np.arange(30, 80, 5)
+
+    x2d, y2d = np.meshgrid(x, y)
+    u = 40 * np.cos(np.deg2rad(y2d))
+    v = 40 * np.cos(2. * np.deg2rad(x2d))
+    mag = (u**2 + v**2)**.5
+    return dict(x=x, y=y, u=u, v=v, magnitude=mag)
+
+
+def main():
+    # Get some data with x, y, u, v, components
+    wind = gen_data()
+
+    # Setup our figure
+    plot_extent = [-60, 40, 30, 70]
+
+    # Arrow plots
+    ax = plt.axes(projection=ccrs.RotatedPole(
+        pole_latitude=45, pole_longitude=180))
+    ax.set_extent(plot_extent, crs=ccrs.PlateCarree())
+    ax.coastlines()
+    ax.stock_img()
+    plt.title('Wind arrows, Rotated Pole projection')
+    ax.quiver(wind['x'], wind['y'], wind['u'], wind['v'], wind['magnitude'],
+              transform=ccrs.PlateCarree())
+
+    plt.show()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/cartopy/examples/barbs.py b/lib/cartopy/examples/barbs.py
new file mode 100644
--- /dev/null
+++ b/lib/cartopy/examples/barbs.py
@@ -0,0 +1,39 @@
+import matplotlib.pyplot as plt
+import numpy as np
+
+import cartopy.crs as ccrs
+
+
+def gen_data():
+    # Generate some data
+    x = np.arange(-60, 45, 5)
+    y = np.arange(30, 80, 5)
+
+    x2d, y2d = np.meshgrid(x, y)
+    u = 40 * np.cos(np.deg2rad(y2d))
+    v = 40 * np.cos(2. * np.deg2rad(x2d))
+    mag = (u**2 + v**2)**.5
+    return dict(x=x, y=y, u=u, v=v, magnitude=mag)
+
+
+def main():
+    # Get some data with x, y, u, v, components
+    wind = gen_data()
+
+    # Setup our figure
+    plot_extent = [-60, 20, 30, 80]
+
+    # Barb plots
+    ax = plt.axes(projection=ccrs.PlateCarree())
+    ax.set_extent(plot_extent, crs=ccrs.PlateCarree())
+    ax.coastlines()
+    ax.barbs(wind['x'], wind['y'], wind['u'], wind['v'], length=6,
+             linewidth=.5)
+    ax.stock_img()
+    plt.title('Wind barbs, native PlateCarree projection')
+
+    plt.show()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/cartopy/examples/streamplot.py b/lib/cartopy/examples/streamplot.py
new file mode 100644
--- /dev/null
+++ b/lib/cartopy/examples/streamplot.py
@@ -0,0 +1,42 @@
+import matplotlib.pyplot as plt
+import numpy as np
+
+import cartopy.crs as ccrs
+
+
+def gen_data():
+    # Generate some data
+    x = np.arange(-60, 45, 5)
+    y = np.arange(30, 80, 5)
+
+    x2d, y2d = np.meshgrid(x, y)
+    u = 40 * np.cos(np.deg2rad(y2d))
+    v = 40 * np.cos(2. * np.deg2rad(x2d))
+    mag = (u**2 + v**2)**.5
+    return dict(x=x, y=y, u=u, v=v, magnitude=mag)
+
+
+def main():
+    # Get some data with x, y, u, v, components
+    wind = gen_data()
+
+    # Setup our figure
+    plot_extent = [-60, 40, 30, 70]
+
+    # Stream plots
+    ax = plt.axes(projection=ccrs.RotatedPole(
+        pole_latitude=45, pole_longitude=180))
+    ax.set_extent(plot_extent, crs=ccrs.PlateCarree())
+    ax.coastlines()
+    ax.stock_img()
+    plt.title('Stream plot, North Polar Stereographic projection')
+    ax.streamplot(wind['x'], wind['y'], wind['u'], wind['v'], density=1,
+                  arrowsize=1, transform=ccrs.PlateCarree(),
+                  linewidth=3 * wind['magnitude']/wind['magnitude'].max(),
+                  color=wind['magnitude'])
+
+    plt.show()
+
+
+if __name__ == '__main__':
+    main()


Test patch: