Skip to content

Conversation

@djkirkham
Copy link
Contributor

@djkirkham djkirkham commented Dec 7, 2016

Plotting functions which use a cube's coordinate's points for the axis values (e.g. contour) convert time coordinate dates into the units used by matplotlib where possible, allowing them to be interpreted correctly by, for example, matplolib.dates.DateFormatters. This PR extends that functionality to plotting functions which use bounds.

@pp-mo
Copy link
Member

pp-mo commented Dec 9, 2016

Is there some hope of showing us what difference this makes ?
I don't know if a test is a good idea, but at least an example on the PR would be nice .
Currently I'm not clear how to exercise this, or what problem it is really solving.

@djkirkham
Copy link
Contributor Author

I've just read my initial description and see how horrible it is. I've updated it, but I'll provide extra info here.

When a 2D cube is plotted with an iris.plot function which uses the coordinates' points (e.g. contour) and one of the dimension coordinates is a time coordinate, here's what happens: the point values are converted into date objects and, if these objects are of type datetime.datetime (as opposed to netcdftime.datetime), they are converted back to numbers using matplotlib.dates.date2num before being passed to the matplotlib plotting function. This means that if one uses an axis formatter then the correct dates are shown.

For plotting functions which use the coordinate bounds values, such as pcolormesh, no such conversion is done, so you get the wrong dates on the axis if you use a date formatter. This PR fixes that.

Here is some example code which demonstrates the difference. Run it with current Iris code and note the difference in dates on the y axis for the two plots:

import matplotlib.dates as mpl_dates
import matplotlib.pyplot as plt
import numpy as np

from iris.coords import DimCoord
from iris.cube import Cube
import iris.plot as iplt

# Construct a cube.
lon = DimCoord([60,180,300], standard_name='longitude', units='degrees')
lon.guess_bounds()
time = DimCoord([10, 20, 30], units='days since 2000-01-01')
time.guess_bounds()
data = np.arange(9).reshape((3,3))
cube = Cube(data, dim_coords_and_dims=[(time, 0), (lon, 1)])

formatter = mpl_dates.DateFormatter('%Y-%m-%d')

iplt.pcolormesh(cube)
plt.gca().yaxis.set_major_formatter(formatter)

plt.figure()
iplt.contourf(cube)
plt.gca().yaxis.set_major_formatter(formatter)

iplt.show()

@djkirkham djkirkham force-pushed the bounds_plot_dates_fix branch 2 times, most recently from a7a102d to e43837b Compare December 12, 2016 09:57
@djkirkham djkirkham force-pushed the bounds_plot_dates_fix branch from e43837b to 0b9241c Compare December 12, 2016 10:05
@djkirkham
Copy link
Contributor Author

I need to update the image hash value for the two failing tests (i've verified them visually). Can someone give me some pointers on how to do that?

@pp-mo
Copy link
Member

pp-mo commented Dec 21, 2016

Your image test failure is also exposing a bug in the idiff.py code :

Traceback (most recent call last):
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/test_plot.py", line 63, in test_bounds
    self.check_graphic()
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/__init__.py", line 790, in check_graphic
    raise ValueError(emsg.format(msg))
ValueError: Image comparison failed: Bad phash 5fa9462be303bcd0addce903a15e5c98a98b036f5ea0077ed22716b606bf5827 with hamming distance [34] for test iris.tests.test_plot.TestSimple.test_bounds.0.
  ...
Ran 3910 tests in 310.687s
FAILED (SKIP=9, errors=2)
Traceback (most recent call last):
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/runner/__main__.py", line 50, in <module>
    runner.run()
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/runner/_runner.py", line 187, in run
    print(failed_images_html())
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/runner/_runner.py", line 51, in failed_images_html
    for expected, actual, diff in step_over_diffs(rdir, 'similar', False):
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/idiff.py", line 213, in step_over_diffs
    hash_index, distance = _calculate_hit(uris, phash, action)
  File "/home/travis/miniconda/envs/test-environment/lib/python2.7/site-packages/Iris-1.12.0.dev0-py2.7-linux-x86_64.egg/iris/tests/idiff.py", line 145, in _calculate_hit
    for uri in uris]
NameError: global name '_HASH_SIZE' is not defined

The global _HASH_SIZE should have been imported from iris.tests (or something like).
However that isn't the only problem here, as I think the temporary-image-results directory location/name has also changed.
It seems we just haven't got this working properly with the imagehash mechanism yet.

I think 'idiff' ought to be the easy way in to adding a new image-hash + its reference image, but we are behind in getting this working and/or documenting it. #2185 was a start on that, but that now needs updating with the consequences of the move from exact image hashes to imagehash-es.
Can you cast any light on how we should be fixing this + promoting it, @bjlittle @marqh ?

values = coord.contiguous_bounds()
values = _fixup_dates(coord, values)
if (isinstance(values.ravel()[0], datetime.datetime)):
values = mpl_dates.date2num(values)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you removed this bit? Does it still work without this? What happens when the values are datetimes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is code I've added.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes of course it is, sorry, it's green.

In which case, I approve, just need to work out where we are with the image hashing stuff now.

@corinnebosley
Copy link
Member

@djkirkham Are these the same steps you followed last time you looked at this ticket: http://scitools.org.uk/iris/docs/latest/developers_guide/graphics_tests.html#developer-graphics-tests
?

@djkirkham
Copy link
Contributor Author

@corinnebosley No, I wasn't aware of that document before. I'll take a look, thanks.

@djkirkham
Copy link
Contributor Author

djkirkham commented May 23, 2017

There's a problem here, and it's one which already exists when plotting dates based on the points. As I said, when Iris plots dates based on points on an axis, it converts the numeric data to datetime objects and back to numbers again. But the new numbers have a different unit to the original.

So if a user has added an axis label like 'Hours since 1st January 2000', the numbers won't match. In fact, if it's plotted using quickplot, an axis label based on the original units will automatically be added.

@corinnebosley
Copy link
Member

@djkirkham Once again, taking this on unless you say otherwise.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants