Skip to content

Transfo manager base#1164

Draft
ajkswamy wants to merge 6 commits into
transformation_managerfrom
transfo_manager_base
Draft

Transfo manager base#1164
ajkswamy wants to merge 6 commits into
transformation_managerfrom
transfo_manager_base

Conversation

@ajkswamy

Copy link
Copy Markdown
Collaborator

@Tomaz-Vieira Tomaz-Vieira left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I left a bunch of mostly typing-related comments, as you might imagine 😆

Also, this is something we might need to decide as a team, but it's a bit funny that often the docstring is way longer than the code itself, and often there isn't much more to say, like:

souce_cs
    The name of the source coordinate system

I often feel like the typing signature is more concise and more informative than the docstrings, and that sometimes we could survive just with the headline and the "raises" part.

But anyway, good stuff, it's nice to see we're moving already =)

"""Initialize a Scene with empty mappings."""
self._coordinate_systems: dict[str, NgffCoordinateSystem] = {}
# mapping names of coordinate system to coordinate systems
self._coordinate_transforms: dict[tuple[str, str], BaseTransformation] = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This likely should be a graph so we can find indirect paths between coordinate systems, and also so we could have multiple different edges between the same 2 CSs. I see below that you've made nx into an optional dependency, but it seems to me like we might need it as a hard dependency.


def __init__(self) -> None:
"""Initialize a Scene with empty mappings."""
self._coordinate_systems: dict[str, NgffCoordinateSystem] = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I wonder if this extra level of indirection (i.e. CS name -> CS) is the best approach. I makes our APIs "stringly" typed, where they take and return str (like in _get_transformations_associated_with_cs) rather than working with something more semantically clear, which is the NgffCoordinateSystem itself.

self._coordinate_transforms: dict[tuple[str, str], BaseTransformation] = {}
# mapping a tuple of coordinate system names, (<source coordinate system name>, <target coordinate system name>)
# to a transformation object representing the transformation between them
self._element_to_cs_mapping: dict[tuple[ELEMENT_TYPE, str], str] = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'll be sounding like a broken record, but the fact that we use strings for everything here (CS name and Element name) is a bit confusing. It seems we could just straight up have a

dict[ELEMENT_TYPE, NgffCoordinateSystem]

which reads easier and type checks more tightly, since there can never be mixups between element names and CS names. We do have to make sure that all elements have reasonable __hash__ and __eq__, though.

associated_transformations.append(transformation_key)
return associated_transformations

def _get_elements_associated_with_cs(self, cs_name: str) -> list[tuple[ELEMENT_TYPE, str]]:

@Tomaz-Vieira Tomaz-Vieira Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe we need a word that is better than "associated" to mean that the element belongs or exists in the coordinate system. It's a stronger relation than the transforms that are "associated" with the coordinate systems, because an element can only belong so a single CS, but a CS can have many transforms.

associated_elements = self._get_elements_associated_with_cs(cs_name)

# Raise error if there are associated transformations or elements
if len(associated_transformations) or len(associated_elements):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I would raise a different exception (or at least an exception with different text) for each situation.


# Raise error if there are associated transformations or elements
if len(associated_transformations) or len(associated_elements):
raise ValueError(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Personally, I like declaring custom exceptions so that users can catch them explicitly, A ValueError is very general and doesn't say much about the kind of failure that happened. If a user wants to try removing a CS and fails, it would be nice if they could except CoordSystemInUse or except CoordSystemMissing to handle the different cases.

raise KeyError(f"Transformation from '{input_cs}' to '{output_cs}' not found.")
del self._coordinate_transforms[key]

def build_nx_graph(self) -> Any: # type: ignore[unresolved-reference] # noqa: F821

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe we could some shenanigans like

if TYPE_CHECKING:
    import networkx as nx

...

def build_nx_graph(self) -> "nx.DiGraph":
    import networkx as nx # it's unfortunate that we need to import again here, though =/
    ...

But I suspect we'd need a hard dependency on this anyway to be compatible with all ways in which ngff can transform.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants