Source code for brainglobe_atlasapi.structure_class

"""
Provide a class for representing hierarchical structures,
such as brain regions in an atlas.
"""

import os
import warnings
from collections import UserDict
from pathlib import Path

import meshio as mio
import s3fs
from fsspec.callbacks import TqdmCallback

from brainglobe_atlasapi.descriptors import remote_url_s3
from brainglobe_atlasapi.structure_tree_util import get_structures_tree


[docs] class Structure(UserDict): """Class implementing the lazy loading of a mesh if the dictionary is queried for it. """ def __getitem__(self, item): """ Retrieve an item from the structure's data. If the item is `mesh` and the mesh data is currently None, it attempts to load the mesh from the `mesh_filename` if available. Parameters ---------- item : str The key of the item to retrieve. Returns ------- meshio.Mesh or None or any - If `item` is "mesh" and the mesh data is successfully loaded, returns a `meshio.Mesh` object. - If `item` is "mesh" and `mesh_filename` is None, returns `None`. - For other keys, returns the value associated with the given item, which can be of any type depending on the stored data. Raises ------ meshio.ReadError If `item` is "mesh" and the mesh cannot be read. The value associated with the given item. """ if item == "mesh" and self.data[item] is None: file_name = self.data["mesh_filename"] if file_name is None: warnings.warn( "No mesh filename for region {}".format( self.data["acronym"] ) ) return None try: self._check_mesh_cached(file_name) self.data[item] = mio.read( file_name, file_format="neuroglancer" ) except (TypeError, mio.ReadError, FileNotFoundError): raise mio.ReadError( "No valid mesh for region: {}".format(self.data["acronym"]) ) return self.data[item] def _check_mesh_cached(self, file_name: Path): """Check if the mesh is cached, and if not, attempt to load it.""" if file_name.exists(): return root_path = "/".join(str(file_name).split(os.sep)[-5:]) remote_mesh_path = remote_url_s3.format(root_path) fs = s3fs.S3FileSystem(anon=True) if not fs.exists(remote_mesh_path): raise FileNotFoundError( f"Mesh file {file_name} not found locally or remotely." ) fs.get(remote_mesh_path, file_name, callback=TqdmCallback())
[docs] class StructuresDict(UserDict): """Class to handle dual indexing by either acronym or id. Parameters ---------- mesh_path : str or Path object path to folder containing all meshes .obj files """ def __init__(self, structures_list): super().__init__() # Acronym to id map: self.acronym_to_id_map = { r["acronym"]: r["id"] for r in structures_list } for struct in structures_list: sid = struct["id"] self.data[sid] = Structure(**struct, mesh=None) self.tree = get_structures_tree(structures_list) def __getitem__(self, item): """Core implementation of the class support for different indexing. Parameters ---------- item : str or int The acronym (str) or id (int) of the requested structure. Returns ------- Structure The Structure requested. """ try: item = int(item) except ValueError: item = self.acronym_to_id_map[item] return self.data[int(item)] def __repr__(self): """Return string representation of the class, showing all region names. """ return self.tree.show(stdout=False)