Source code for gridgeo.ugrid

"""
Lightweight UGRID-1.0 parser

"""

import netCDF4
import numpy as np


def _valid_x(var):
    names = ["longitude", "grid_longitude", "projection_x_coordinate"]

    units = [
        "degrees_east",
        "degree_east",
        "degree_E",
        "degrees_E",
        "degreeE",
        "degreesE",
    ]
    if getattr(var, "standard_name", None) in names:
        return True
    if getattr(var, "axis", "None").lower() == "x":
        return True
    # Units are mandatory, fail if not present.
    if var.units in units:
        return True
    return False


def _valid_y(var):
    names = ["latitude", "grid_latitude", "projection_y_coordinate"]

    units = [
        "degrees_north",
        "degree_north",
        "degree_N",
        "degrees_N",
        "degreeN",
        "degreesN",
    ]

    if getattr(var, "standard_name", None) in names:
        return True
    if getattr(var, "axis", "None").lower() == "y":
        return True
    # Units are mandatory, fail if not present.
    if var.units in units:
        return True
    return False


def _mandatory_attr(var, attribute):
    if not hasattr(var, attribute):
        raise ValueError(
            f"Could not find required attribute {attribute} in {var}."
        )
    return


[docs]def get_mesh_var(nc): """Returns the mesh_topology variable for `nc` (netCDF4.Dataset object).""" mesh_var = nc.get_variables_by_attributes(cf_role="mesh_topology") if not mesh_var: raise ValueError( f"Could not find mesh_topology variable in the dataset {nc}" ) if len(mesh_var) > 1: raise ValueError( f"Expected 1 mesh_topology variable, found {len(mesh_var)}." ) mesh_var = mesh_var[0] _mandatory_attr(mesh_var, attribute="node_coordinates") _mandatory_attr(mesh_var, attribute="topology_dimension") if mesh_var.topology_dimension not in (1, 2): raise ValueError( f"Expected mesh dimension to be 1 or 2, got {mesh_var.topology_dimension}." ) return mesh_var
[docs]def connectivity_array(connectivity, num_ind): """Returns the connectivity array for its correspdonding `netCDF4.Variable` according to UGRID-1.0. """ array = connectivity[:] if not issubclass(array.dtype.type, np.integer): array = np.int_(array) if array.shape[0] == num_ind: array = array.T start_index = int(getattr(connectivity, "start_index", 0)) if start_index >= 1: array -= start_index # FIXME: This won't work for more than one flag value. flag_values = getattr(connectivity, "flag_values", None) if flag_values: array[array == flag_values - start_index] = flag_values return array
[docs]def ugrid(nc): """Parse UGRID conventions. Take a netCDF4.Dataset object or a netCDF4 file/url string and returns a dictionary with the grid nodes, edges, and connectivy matrix. """ if isinstance(nc, netCDF4.Dataset): pass else: nc = netCDF4.Dataset(nc) mesh_var = get_mesh_var(nc) valid_coords = ( "node_coordinates", "face_coordinates", "edge_coordinates", "boundary_coordinates", ) valid_connectivity = { "face_node_connectivity": 3, "face_face_connectivity": 3, "boundary_node_connectivity": 2, "edge_node_connectivity": 2, } # Used for compatibility with pyugrid. rename = { "node_coordinates": "nodes", "face_node_connectivity": "faces", "boundary_node_connectivity": "boundaries", "edge_node_connectivity": "edges", } grid = {} for key, value in mesh_var.__dict__.items(): if key in valid_coords: coord_names = mesh_var.getncattr(key).strip().split() for name in coord_names: if _valid_x(nc[name]): x = nc[name][:] elif _valid_y(nc[name]): y = nc[name][:] else: raise ValueError( f"Could not recognize axis for {nc[name]}" ) grid.update({key: {"x": x, "y": y}}) if key in valid_connectivity.keys(): connectivity = nc[mesh_var.getncattr(key).strip()] num_ind = valid_connectivity[key] array = connectivity_array(connectivity, num_ind) grid.update({key: array}) return {rename.get(k, k): v for k, v in grid.items()}