Source code for osbng.hierarchy

"""Navigate the hierarchical structure of the BNG index system.

The British National Grid (BNG) is structured using a hierarchical system of grid
squares at various resolutions. At its highest level, the grid divides GB into 100 km by
100 km squares, each identified by a two-letter code. Successive levels of resolution
further subdivide the grid squares into finer detail, down to individual 1-meter
squares. This module allows for the traversal of this hierarchy by providing methods to
return the parent and children of :class:`~osbng.bng_reference.BNGReference` objects at
specified resolutions.

Parent and child definitions:
    - **Parent**: The parent of a :class:`~osbng.bng_reference.BNGReference` object is
      the grid square at the next higher (coarser) resolution level that contains the
      current reference. For example, the parent of a 1km grid square reference would be
      the 5km grid square that contains it.
    - **Children**: The children of a :class:`~osbng.bng_reference.BNGReference` object
      are the grid squares at the next lower (finer) resolution level that are contained
      within the current reference. For example, the children of a 10km grid square
      reference would be the 5km grid squares that it contains.

Notes:
    While parent and child derivation defaults to the next higher and lower
    resolution, any supported resolution in the hierarchy can be specified.

Supported Resolutions:
    - The module supports the 'standard' and 'intermediate' quadtree resolutions,
      including ``100km``, ``50km``, ``10km``, ``5km``, ``1km``, ``500m``, ``100m``,
      ``50m``, ``10m``, ``5m`` and ``1m``.
    - These resolutions passed to hierarchy functions are validated and normalised
      using the resolution mapping defined in the :doc:`resolution` module.
"""

from osbng.bng_reference import BNGReference, _validate_bngreferences
from osbng.errors import BNGHierarchyError
from osbng.indexing import (
    _validate_and_normalise_bng_resolution,
    bbox_to_bng,
    bng_to_xy,
    xy_to_bng,
)
from osbng.resolution import BNG_RESOLUTIONS

__all__ = ["bng_to_children", "bng_to_parent"]


[docs] @_validate_bngreferences def bng_to_children( bng_ref: BNGReference, *, resolution: int | str | None = None ) -> list[BNGReference]: """Returns a list of child ``BNGReference`` objects of a ``BNGReference``. By default, the children of the :class:`~osbng.bng_reference.BNGReference` object is defined as the :class:`~osbng.bng_reference.BNGReference` objects in the next resolution down from the input :class:`~osbng.bng_reference.BNGReference` resolution . For example, 100km -> 50km. Notes: Any valid resolution can be provided as the child resolution, provided it is less than the resolution of the input :class:`~osbng.bng_reference.BNGReference` . Args: bng_ref (BNGReference): The :class:`~osbng.bng_reference.BNGReference` object to derive children from. Keyword Args: resolution (int | str | None, optional): The resolution of the children :class:`~osbng.bng_reference.BNGReference` objects expressed either as a metre-based integer or as a string label. Defaults to None. Returns: list[BNGReference]: A list of :class:`~osbng.bng_reference.BNGReference` objects that are children of the input :class:`~osbng.bng_reference.BNGReference` object . Raises: BNGReferenceError: If the first positional argument is not a :class:`~osbng.bng_reference.BNGReference` object. BNGHierarchyError: If the resolution of the input :class:`~osbng.bng_reference.BNGReference` object is 1m. BNGHierarchyError: If the resolution is greater than or equal to the resolution of the input :class:`~osbng.bng_reference.BNGReference` object. BNGResolutionError: If an invalid resolution is provided. Examples: >>> bng_to_children(BNGReference("SU")) [BNGReference(bng_ref_formatted=SU SW, resolution_label=50km), BNGReference(bng_ref_formatted=SU SE, resolution_label=50km), BNGReference(bng_ref_formatted=SU NW, resolution_label=50km), BNGReference(bng_ref_formatted=SU NE, resolution_label=50km)] >>> bng_to_children(BNGReference("SU36")) [BNGReference(bng_ref_formatted=SU 3 6 SW, resolution_label=5km), BNGReference(bng_ref_formatted=SU 3 6 SE, resolution_label=5km), BNGReference(bng_ref_formatted=SU 3 6 NW, resolution_label=5km), BNGReference(bng_ref_formatted=SU 3 6 NE, resolution_label=5km)] See Also: - The equivalent :meth:`osbng.bng_reference.BNGReference.bng_to_children` instance method. - :class:`~osbng.hierarchy.bng_to_parent` to derive the :class:`~osbng.bng_reference.BNGReference` object in the next resolution up. """ # Raise error if the resolution is 1m if bng_ref.resolution_metres == 1: raise BNGHierarchyError("Cannot derive children from the finest 1m resolution") # Generate a scaled resolution if none is provided if resolution is None: resolution = bng_ref.resolution_metres if BNG_RESOLUTIONS[resolution]["quadtree"]: resolution = int((bng_ref.resolution_metres / 5)) else: resolution = int((bng_ref.resolution_metres / 2)) # Validate and normalise the resolution to its metre-based integer value validated_resolution = _validate_and_normalise_bng_resolution(resolution) # Raise error if the validated resolution is greater than the resolution of the # input BNGReference object if validated_resolution >= bng_ref.resolution_metres: raise BNGHierarchyError( "Resolution must be less than the resolution of input BNGReference object" ) # Get min and max coordinates of the grid square bounding box min_coords = bng_to_xy(bng_ref, position="lower-left") max_coords = bng_to_xy(bng_ref, position="upper-right") # Derive children BNGReference objects from the bounding box bng_refs = bbox_to_bng( min_coords[0], min_coords[1], max_coords[0], max_coords[1], validated_resolution ) return bng_refs
[docs] @_validate_bngreferences def bng_to_parent( bng_ref: BNGReference, *, resolution: int | str | None = None ) -> BNGReference: """Returns the `BNGReference`that is the parent of a ``BNGReference``. By default, the parent of the :class:`~osbng.bng_reference.BNGReference` object is defined as the :class:`~osbng.bng_reference.BNGReference` in the next BNG resolution up from the input :class:`~osbng.bng_reference.BNGReference` resolution. For example , 50km -> 100km. Notes: Any valid resolution can be provided as the parent resolution, provided it is greater than the resolution of the input :class:`~osbng.bng_reference.BNGReference`. Args: bng_ref (BNGReference): The :class:`~osbng.bng_reference.BNGReference` object to derive parent from. Keyword Args: resolution (int | str | None, optional): The resolution of the parent :class:`~osbng.bng_reference.BNGReference` objects expressed either as a metre-based integer or as a string label. Defaults to None. Returns: BNGReference: A :class:`~osbng.bng_reference.BNGReference` object that is the parent of the input :class:`~osbng.bng_reference.BNGReference` object. Raises: BNGReferenceError: If the first positional argument is not a :class:`~osbng.bng_reference.BNGReference` object. BNGHierarchyError: If the resolution of the input :class:`~osbng.bng_reference.BNGReference` object is 100km. BNGHierarchyError: If the resolution is less than or equal to the resolution of the input :class:`~osbng.bng_reference.BNGReference` object. BNGResolutionError: If an invalid resolution is provided. Examples: >>> bng_to_parent(BNGReference("SU 3 6 SW")) BNGReference(bng_ref_formatted=SU 3 6, resolution_label=10km) >>> bng_to_parent(BNGReference("SU 342 567")) BNGReference(bng_ref_formatted=SU 34 56 NW, resolution_label=500m) >>> bng_to_parent(BNGReference("SU 342 567"), resolution=10000) BNGReference(bng_ref_formatted=SU 3 5, resolution_label=10km) See Also: - The equivalent :meth:`osbng.bng_reference.BNGReference.bng_to_parent` instance method. - :class:`~osbng.hierarchy.bng_to_children` to derive the :class:`~osbng.bng_reference.BNGReference` objects in the next resolution down. """ # Raise error if the resolution is 100km if bng_ref.resolution_metres == 100000: raise BNGHierarchyError( "Cannot derive parent from the coarsest 100km resolution" ) # Generate a scaled resolution if none is provided if resolution is None: resolution = bng_ref.resolution_metres if BNG_RESOLUTIONS[resolution]["quadtree"]: resolution = int((bng_ref.resolution_metres * 2)) else: resolution = int((bng_ref.resolution_metres * 5)) # Validate and normalise the resolution to its metre-based integer value validated_resolution = _validate_and_normalise_bng_resolution(resolution) # Raise error if the validated resolution is less than the resolution of the input # BNGReference object if validated_resolution <= bng_ref.resolution_metres: raise BNGHierarchyError( "Resolution must be greater than the resolution of input BNGReference " "object" ) # Dervive coordinates of the grid square bounding box x, y = bng_to_xy(bng_ref, position="lower-left") # Derive parent BNGReference object from coordinates bng_ref = xy_to_bng(x, y, validated_resolution) return bng_ref