-
Notifications
You must be signed in to change notification settings - Fork 284
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
Pycarto++ #119
Pycarto++ #119
Changes from all commits
1f3b37f
c96a699
3480056
8b21acb
b6f980e
a6e3af2
2c66d0f
15d3bd4
558d0b4
10c84de
0bb2366
cf69468
2789ca0
7bdea1e
37a0224
bcb51b0
23a9e28
16f67e9
304f660
7ef4f36
63f9d04
6bbecb9
8b2fc53
7abf2b9
e221228
2679f3b
19f05c0
4160064
7434e1a
3bec31c
c1e8c50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,8 +22,9 @@ | |
import math | ||
import warnings | ||
|
||
from mpl_toolkits.basemap import pyproj | ||
import pyproj | ||
import numpy | ||
import cartopy.crs | ||
|
||
import iris.analysis | ||
import iris.coords | ||
|
@@ -95,92 +96,106 @@ def _get_lat_lon_coords(cube): | |
lat_coords = filter(lambda coord: "latitude" in coord.name(), cube.coords()) | ||
lon_coords = filter(lambda coord: "longitude" in coord.name(), cube.coords()) | ||
if len(lat_coords) > 1 or len(lon_coords) > 1: | ||
raise ValueError("Calling lat_lon_range() with multiple lat or lon coords is currently disallowed") | ||
raise ValueError("Calling _get_lat_lon_coords() with multiple lat or lon coords is currently disallowed") | ||
lat_coord = lat_coords[0] | ||
lon_coord = lon_coords[0] | ||
return (lat_coord, lon_coord) | ||
|
||
|
||
def lat_lon_range(cube, mode=None): | ||
def xy_range(cube, mode=None, projection=None): | ||
""" | ||
Return the lat & lon range of this Cube. | ||
Return the x & y range of this Cube. | ||
|
||
If the coordinate has both points & bounds, the mode keyword can be set to determine which should be | ||
used in the min/max calculation. (Must be one of iris.coords.POINT_MODE or iris.coords.BOUND_MODE) | ||
Args: | ||
|
||
* cube - The cube for which to calculate xy extents. | ||
|
||
Kwargs: | ||
|
||
* mode - If the coordinate has bounds, use the mode keyword to specify the | ||
min/max calculation (iris.coords.POINT_MODE or iris.coords.BOUND_MODE). | ||
|
||
* projection - Calculate the xy range in an alternative projection. | ||
|
||
""" | ||
# Helpful error if we have an inappropriate CoordSystem | ||
cs = cube.coord_system("CoordSystem") | ||
if cs is not None and not isinstance(cs, (iris.coord_systems.GeogCS, iris.coord_systems.RotatedGeogCS)): | ||
raise ValueError("Latlon coords cannot be found with {0}.".format(type(cs))) | ||
|
||
# get the lat and lon coords (might have "grid_" at the start of the name, if rotated). | ||
lat_coord, lon_coord = _get_lat_lon_coords(cube) | ||
x_coord, y_coord = cube.coord(axis="X"), cube.coord(axis="Y") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yet more use of axis... (I think I wrote this bit in the first place). Getting the x and y coordinates is by far the biggest use case for "axis" usage. Is it worth looking into providing a more elegant solution? |
||
cs = cube.coord_system('CoordSystem') | ||
|
||
if lon_coord.has_bounds() != lat_coord.has_bounds(): | ||
raise ValueError('Cannot get the range of the latitude and longitude coordinates if they do ' | ||
if x_coord.has_bounds() != x_coord.has_bounds(): | ||
raise ValueError('Cannot get the range of the x and y coordinates if they do ' | ||
'not have the same presence of bounds.') | ||
|
||
if lon_coord.has_bounds(): | ||
if x_coord.has_bounds(): | ||
if mode not in [iris.coords.POINT_MODE, iris.coords.BOUND_MODE]: | ||
raise ValueError('When the coordinate has bounds, please specify "mode".') | ||
_mode = mode | ||
else: | ||
_mode = iris.coords.POINT_MODE | ||
|
||
# Get the x and y grids | ||
if isinstance(cs, iris.coord_systems.RotatedGeogCS): | ||
if _mode == iris.coords.POINT_MODE: | ||
lats, lons = get_lat_lon_grids(cube) | ||
x, y = get_xy_grids(cube) | ||
else: | ||
lats, lons = get_lat_lon_contiguous_bounded_grids(cube) | ||
x, y = get_xy_contiguous_bounded_grids(cube) | ||
else: | ||
if _mode == iris.coords.POINT_MODE: | ||
lons = lon_coord.points | ||
lats = lat_coord.points | ||
x = x_coord.points | ||
y = y_coord.points | ||
else: | ||
lons = lon_coord.bounds | ||
lats = lat_coord.bounds | ||
x = x_coord.bounds | ||
y = y_coord.bounds | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This api change should be documented as a non-compatible change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Similarly for |
||
if projection: | ||
# source projection | ||
source_cs = cube.coord_system("CoordSystem") | ||
if source_cs is not None: | ||
source_proj = source_cs.as_cartopy_projection() | ||
else: | ||
#source_proj = cartopy.crs.PlateCarree() | ||
raise Exception('Unknown source coordinate system') | ||
|
||
if source_proj != projection: | ||
# TODO: Ensure there is a test for this | ||
x, y = projection.transform_points(x=x, y=y, src_crs=source_proj) | ||
|
||
if getattr(lon_coord, 'circular', False): | ||
lon_range = (numpy.min(lons), numpy.min(lons) + lon_coord.units.modulus) | ||
# Get the x and y range | ||
if getattr(x_coord, 'circular', False): | ||
x_range = (numpy.min(x), numpy.min(x) + x_coord.units.modulus) | ||
else: | ||
lon_range = (numpy.min(lons), numpy.max(lons)) | ||
return ( (numpy.min(lats), numpy.max(lats)), lon_range ) | ||
x_range = (numpy.min(x), numpy.max(x)) | ||
|
||
y_range = (numpy.min(y), numpy.max(y)) | ||
|
||
return (x_range, y_range) | ||
|
||
def get_lat_lon_grids(cube): | ||
|
||
def get_xy_grids(cube): | ||
""" | ||
Return 2d lat and lon points in the requested coordinate system. | ||
Return 2d x and y points in the native coordinate system. | ||
:: | ||
|
||
lats, lons = get_lat_lon_grids(cube) | ||
x, y = get_xy_grids(cube) | ||
|
||
""" | ||
# get the lat and lon coords (might have "grid_" at the start of the name, if rotated). | ||
lat_coord, lon_coord = _get_lat_lon_coords(cube) | ||
x_coord, y_coord = cube.coord(axis="X"), cube.coord(axis="Y") | ||
cs = cube.coord_system('CoordSystem') | ||
|
||
if lon_coord.units != 'degrees': | ||
lon_coord = lon_coord.unit_converted('degrees') | ||
if lat_coord.units != 'degrees': | ||
lat_coord = lat_coord.unit_converted('degrees') | ||
|
||
lons = lon_coord.points | ||
lats = lat_coord.points | ||
x = x_coord.points | ||
y = y_coord.points | ||
|
||
# Convert to 2 x 2d grid of data | ||
lons, lats = numpy.meshgrid(lons, lats) | ||
|
||
# if the pole was rotated, then un-rotate it | ||
if isinstance(cs, iris.coord_systems.RotatedGeogCS): | ||
lons, lats = unrotate_pole(lons, lats, cs.grid_north_pole_longitude, cs.grid_north_pole_latitude) | ||
|
||
return (lats, lons) | ||
x, y = numpy.meshgrid(x, y) | ||
|
||
return (x, y) | ||
|
||
def get_lat_lon_contiguous_bounded_grids(cube): | ||
|
||
def get_xy_contiguous_bounded_grids(cube): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should look at how many places use these functions in "analysis/cartography" - they aren't doing any analysis/cartography, more like utility functions for working with a cube. If it is the case that they are limited to plotting, then I think we should look at moving them there and making them private (or inline). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So...
And So right now these are just mapping support functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Forked issue as #129. |
||
""" | ||
Return 2d lat and lon bounds. | ||
|
||
|
@@ -190,21 +205,14 @@ def get_lat_lon_contiguous_bounded_grids(cube): | |
lats, lons = cs.get_lat_lon_bounded_grids() | ||
|
||
""" | ||
# get the lat and lon coords (might have "grid_" at the start of the name, if rotated). | ||
lat_coord, lon_coord = _get_lat_lon_coords(cube) | ||
x_coord, y_coord = cube.coord(axis="X"), cube.coord(axis="Y") | ||
cs = cube.coord_system('CoordSystem') | ||
|
||
if lon_coord.units != 'degrees': | ||
lon_coord = lon_coord.unit_converted('degrees') | ||
if lat_coord.units != 'degrees': | ||
lat_coord = lat_coord.unit_converted('degrees') | ||
|
||
lons = lon_coord.contiguous_bounds() | ||
lats = lat_coord.contiguous_bounds() | ||
lons, lats = numpy.meshgrid(lons, lats) | ||
if isinstance(cs, iris.coord_systems.RotatedGeogCS): | ||
lons, lats = iris.analysis.cartography.unrotate_pole(lons, lats, cs.grid_north_pole_longitude, cs.grid_north_pole_latitude) | ||
return (lats, lons) | ||
x = x_coord.contiguous_bounds() | ||
y = y_coord.contiguous_bounds() | ||
x, y = numpy.meshgrid(x, y) | ||
|
||
return (x, y) | ||
|
||
|
||
def _quadrant_area(radian_colat_bounds, radian_lon_bounds, radius_of_earth): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the reason I want to get rid of
map_setup
. As a function it is completely redundant other than for setting up a map given a cube (3 cases: cube projection any limits; cube limits any projection; cube limits and cube projection;), which I feel could be done better if we had an interface such as:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, with the change to cartopy
map_setup
is now an unwanted "appendix". I'd rather remove it in a separate PR though.