Skip to content

Commit

Permalink
Merge remote-tracking branch 'HGWright/geo-bridge' into additions_5740
Browse files Browse the repository at this point in the history
  • Loading branch information
trexfeathers committed Mar 27, 2024
2 parents db83c67 + cb11963 commit f8d85b0
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 103 deletions.
16 changes: 8 additions & 8 deletions docs/src/further_topics/ugrid/operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ easy to explore the data in detail.

Performing GeoVista operations on your :class:`~iris.cube.Cube` is made
easy via this convenience:
:func:`iris.experimental.geovista.cube_faces_to_polydata`.
:func:`iris.experimental.geovista.cube_to_polydata`.

Below is an example of using GeoVista to plot a low-res
sample :attr:`~iris.cube.Cube.mesh` based :class:`~iris.cube.Cube`. For
Expand All @@ -492,7 +492,7 @@ GeoVista :external+geovista:doc:`generated/gallery/index`.
>>> import matplotlib.pyplot as plt
>>> from iris import load_cube, sample_data_path
>>> from iris.experimental.geovista import cube_faces_to_polydata
>>> from iris.experimental.geovista import cube_to_polydata
>>> from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
>>> with PARSE_UGRID_ON_LOAD.context():
Expand All @@ -511,7 +511,7 @@ GeoVista :external+geovista:doc:`generated/gallery/index`.
nco_openmp_thread_number 1
# Convert our mesh+data to a PolyData object.
>>> face_polydata = cube_faces_to_polydata(sample_mesh_cube)
>>> face_polydata = cube_to_polydata(sample_mesh_cube)
>>> print(face_polydata)
PolyData (...
N Cells: 96
Expand Down Expand Up @@ -580,10 +580,10 @@ selected region. The recommended way to do this is using tools provided by
Performing GeoVista operations on your :class:`~iris.cube.Cube` is made
easy via this convenience:
:func:`iris.experimental.geovista.cube_faces_to_polydata`.
:func:`iris.experimental.geovista.cube_to_polydata`.
An Iris convenience for regional extraction is also provided:
:func:`iris.experimental.geovista.region_extraction`; demonstrated
:func:`iris.experimental.geovista.extract_unstructured_region`; demonstrated
below:
Expand All @@ -594,7 +594,7 @@ below:
>>> from geovista.geodesic import BBox
>>> from iris import load_cube, sample_data_path
>>> from iris.experimental.geovista import cube_faces_to_polydata, region_extraction
>>> from iris.experimental.geovista import cube_to_polydata, extract_unstructured_region
>>> from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
>>> with PARSE_UGRID_ON_LOAD.context():
Expand All @@ -612,9 +612,9 @@ below:
history 'Mon Apr 12 01:44:41 2021: ncap2 -s synthetic=float(synthetic) mesh_C4_synthetic.nc ...'
nco_openmp_thread_number 1
>>> regional_cube = region_extraction(
>>> regional_cube = extract_unstructured_region(
... cube=sample_mesh_cube,
... polydata=cube_faces_to_polydata(sample_mesh_cube),
... polydata=cube_to_polydata(sample_mesh_cube),
... region=BBox(lons=[0, 70, 70, 0], lats=[-25, -25, 45, 45]),
... preference="center",
... )
Expand Down
40 changes: 23 additions & 17 deletions lib/iris/experimental/geovista.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def _get_coord(cube, axis):
return coord


def cube_faces_to_polydata(cube, **kwargs):
def cube_to_polydata(cube, **kwargs):
r"""Create a :class:`pyvista.PolyData` object from a :class:`~iris.cube.Cube`.
The resulting :class:`~pyvista.PolyData` object can be plotted using
Expand Down Expand Up @@ -66,14 +66,14 @@ def cube_faces_to_polydata(cube, **kwargs):
with PARSE_UGRID_ON_LOAD.context():
cube_mesh = load_cube(sample_data_path("mesh_C4_synthetic_float.nc"))
>>> from iris.experimental.geovista import cube_faces_to_polydata
>>> from iris.experimental.geovista import cube_to_polydata
Converting a standard 2-dimensional :class:`~iris.cube.Cube` with
1-dimensional coordinates:
>>> print(cube.summary(shorten=True))
air_temperature / (K) (latitude: 73; longitude: 96)
>>> print(cube_faces_to_polydata(cube))
>>> print(cube_to_polydata(cube))
PolyData (...
N Cells: 7008
N Points: 7178
Expand All @@ -85,7 +85,7 @@ def cube_faces_to_polydata(cube, **kwargs):
Configure the conversion by passing additional keyword arguments:
>>> print(cube_faces_to_polydata(cube, radius=2))
>>> print(cube_to_polydata(cube, radius=2))
PolyData (...
N Cells: 7008
N Points: 7178
Expand All @@ -100,7 +100,7 @@ def cube_faces_to_polydata(cube, **kwargs):
>>> print(cube_mesh.summary(shorten=True))
synthetic / (1) (-- : 96)
>>> print(cube_faces_to_polydata(cube_mesh))
>>> print(cube_to_polydata(cube_mesh))
PolyData (...
N Cells: 96
N Points: 98
Expand All @@ -115,7 +115,7 @@ def cube_faces_to_polydata(cube, **kwargs):
>>> print(cube_w_time.summary(shorten=True))
air_temperature / (K) (time: 240; latitude: 37; longitude: 49)
>>> print(cube_faces_to_polydata(cube_w_time[0, :, :]))
>>> print(cube_to_polydata(cube_w_time[0, :, :]))
PolyData (...
N Cells: 1813
N Points: 1900
Expand All @@ -142,6 +142,7 @@ def cube_faces_to_polydata(cube, **kwargs):
start_index=face_node.start_index,
**kwargs,
)
# TODO: Add support for point clouds
elif cube.ndim == 2:
x_coord = _get_coord(cube, "X")
y_coord = _get_coord(cube, "Y")
Expand Down Expand Up @@ -170,7 +171,7 @@ def cube_faces_to_polydata(cube, **kwargs):
return polydata


def region_extraction(cube, polydata, region, **kwargs):
def extract_unstructured_region(cube, polydata, region, **kwargs):
"""Index a :class:`~iris.cube.Cube` with a :attr:`~iris.cube.Cube.mesh` to a specific region.
Uses :meth:`geovista.geodesic.BBox.enclosed` to identify the `cube` indices
Expand All @@ -185,7 +186,7 @@ def region_extraction(cube, polydata, region, **kwargs):
A :class:`~pyvista.PolyData` representing the same horizontal space as
`cube`. The region extraction is first applied to `polydata`, with the
resulting indices then applied to `cube`. In many cases `polydata` can
be created by applying :func:`cube_faces_to_polydata` to `cube`.
be created by applying :func:`cube_to_polydata` to `cube`.
region : :class:`geovista.geodesic.BBox`
A :class:`~geovista.geodesic.BBox` representing the region to be
extracted.
Expand Down Expand Up @@ -227,18 +228,18 @@ def region_extraction(cube, polydata, region, **kwargs):
cube_w_mesh = level_cubes.merge_cube()
other_cube_w_mesh = cube_w_mesh[:20, :]
The parameters of :func:`region_extraction` have been designed with
The parameters of :func:`extract_unstructured_region` have been designed with
flexibility and reuse in mind. This is demonstrated below.
>>> from geovista import BBox
>>> from iris.experimental.geovista import cube_faces_to_polydata, region_extraction
>>> from iris.experimental.geovista import cube_to_polydata, extract_unstructured_region
>>> print(cube_w_mesh.shape)
(72, 96)
>>> # The mesh dimension represents the horizontal space of the cube.
>>> print(cube_w_mesh.shape[cube_w_mesh.mesh_dim()])
96
>>> cube_polydata = cube_faces_to_polydata(cube_w_mesh[0, :])
>>> extracted_cube = region_extraction(
>>> cube_polydata = cube_to_polydata(cube_w_mesh[0, :])
>>> extracted_cube = extract_unstructured_region(
... cube=cube_w_mesh,
... polydata=cube_polydata,
... region=BBox(lons=[0, 70, 70, 0], lats=[-25, -25, 45, 45]),
Expand All @@ -249,7 +250,7 @@ def region_extraction(cube, polydata, region, **kwargs):
Now reuse the same `cube` and `polydata` to extract a different region:
>>> new_region = BBox(lons=[0, 35, 35, 0], lats=[-25, -25, 45, 45])
>>> extracted_cube = region_extraction(
>>> extracted_cube = extract_unstructured_region(
... cube=cube_w_mesh,
... polydata=cube_polydata,
... region=new_region,
Expand All @@ -262,7 +263,7 @@ def region_extraction(cube, polydata, region, **kwargs):
>>> print(other_cube_w_mesh.shape)
(20, 96)
>>> extracted_cube = region_extraction(
>>> extracted_cube = extract_unstructured_region(
... cube=other_cube_w_mesh,
... polydata=cube_polydata,
... region=new_region,
Expand All @@ -273,7 +274,7 @@ def region_extraction(cube, polydata, region, **kwargs):
Arbitrary keywords can be passed down to
:meth:`geovista.geodesic.BBox.enclosed` (``outside`` in this example):
>>> extracted_cube = region_extraction(
>>> extracted_cube = extract_unstructured_region(
... cube=other_cube_w_mesh,
... polydata=cube_polydata,
... region=new_region,
Expand All @@ -296,11 +297,16 @@ def region_extraction(cube, polydata, region, **kwargs):
polydata_length = polydata.GetNumberOfPoints()
indices_key = VTK_POINT_IDS
else:
raise NotImplementedError("Must be on face or node.")
raise NotImplementedError(
f"Must be on face or node. Found: {cube.location}."
)

if cube.shape[mesh_dim] != polydata_length:
raise ValueError(
"The mesh on the cube and the polydata must have the" " same shape."
f"The mesh on the cube and the polydata"
f"must have the same shape."
f" Found Mesh: {cube.shape[mesh_dim]},"
f" Polydata: {polydata_length}."
)

region_polydata = region.enclosed(polydata, **kwargs)
Expand Down
5 changes: 5 additions & 0 deletions lib/iris/tests/integration/experimental/geovista/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Integration tests for the :mod:`iris.experimental.geovista` module."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Integration tests for the `iris.experimental.geovista.cube_to_polydata` function."""

import numpy as np

from iris import load_cube
from iris.experimental.geovista import cube_to_polydata
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
from iris.tests import get_data_path


def test_integration_2d():
file_path = get_data_path(
[
"NetCDF",
"ORCA2",
"votemper.nc",
]
)
with PARSE_UGRID_ON_LOAD.context():
global_cube = load_cube(file_path, "votemper")

polydata = cube_to_polydata(global_cube[0, 1, :])

assert polydata.GetNumberOfCells() == 26640
assert polydata.GetNumberOfPoints() == 26969


def test_integration_1d():
file_path = get_data_path(
[
"NetCDF",
"global",
"xyt",
"SMALL_hires_wind_u_for_ipcc4.nc",
]
)
with PARSE_UGRID_ON_LOAD.context():
global_cube = load_cube(file_path)

polydata = cube_to_polydata(global_cube[0, :])

assert polydata.GetNumberOfCells() == 51200
assert polydata.GetNumberOfPoints() == 51681


def test_integration_mesh():
file_path = get_data_path(
[
"NetCDF",
"unstructured_grid",
"lfric_ngvat_2D_72t_face_half_levels_main_conv_rain.nc",
]
)

with PARSE_UGRID_ON_LOAD.context():
global_cube = load_cube(file_path, "conv_rain")

polydata = cube_to_polydata(global_cube[0, :])

assert polydata.GetNumberOfCells() == 864
assert polydata.GetNumberOfPoints() == 866
np.testing.assert_array_equal(polydata.active_scalars, global_cube[0, :].data)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Integration tests for the `iris.experimental.geovista.extract_unstructured_region` function."""

from geovista.geodesic import BBox

from iris import load_cube
from iris.experimental.geovista import cube_to_polydata, extract_unstructured_region
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
from iris.tests import get_data_path


def test_face_region_extraction():
file_path = get_data_path(
[
"NetCDF",
"unstructured_grid",
"lfric_ngvat_2D_72t_face_half_levels_main_conv_rain.nc",
]
)

with PARSE_UGRID_ON_LOAD.context():
global_cube = load_cube(file_path, "conv_rain")
polydata = cube_to_polydata(global_cube[0, :])
region = BBox(lons=[0, 70, 70, 0], lats=[-25, -25, 45, 45])

extracted_cube = extract_unstructured_region(
global_cube, polydata, region, preference="center"
)

assert extracted_cube.ndim == 2
assert extracted_cube.shape == (72, 101)
47 changes: 0 additions & 47 deletions lib/iris/tests/integration/experimental/test_cube_to_poly.py

This file was deleted.

0 comments on commit f8d85b0

Please sign in to comment.