|
| 1 | +from typing import Tuple, Union |
| 2 | + |
| 3 | +import numpy |
| 4 | + |
| 5 | +from gridkit.base_grid import BaseGrid |
| 6 | +from gridkit.gridkit_rs import PyO3HexTile, PyO3RectTile, PyO3TriTile |
| 7 | +from gridkit.hex_grid import HexGrid |
| 8 | +from gridkit.index import GridIndex |
| 9 | +from gridkit.rect_grid import RectGrid |
| 10 | +from gridkit.tri_grid import TriGrid |
| 11 | + |
| 12 | + |
| 13 | +class Tile: |
| 14 | + """A Tile describes a set of cells defined by the ``start_id``, |
| 15 | + which is the cell that defines the bottom-left corner of the tile, |
| 16 | + and ``nx`` and ``ny``, which are the number of cells in the x and y directions, respectively. |
| 17 | +
|
| 18 | + Each tile is associated with a particular grid and the Tile refers to a selection |
| 19 | + of grid indices on that grid. The associated grid can be accessed using the ``.grid`` property. |
| 20 | +
|
| 21 | + .. Note :: |
| 22 | +
|
| 23 | + ``nx`` and ``ny`` can be seen as 'right' and 'up', respectively, when the rotation of the grid is zero. |
| 24 | + If the grid is rotated, the tile rotates with it (naturally). |
| 25 | + This means that for a grid that is rotated 90 degrees, |
| 26 | + ``nx`` refers to the number of cells up, and ``ny`` refers to the number of cells to the left. |
| 27 | +
|
| 28 | + .. |
| 29 | +
|
| 30 | + Init parameters |
| 31 | + --------------- |
| 32 | + grid: :class:`BaseGrid` |
| 33 | + The :class:`.TriGrid`, :class:`.RectGrid` or :class:`.HexGrid` the tile is associated with |
| 34 | + start_id: Union[Tuple[int, int], GridIndex] |
| 35 | + The starting cell of the Tile. |
| 36 | + The starting cell defines the bottom-left corner of the Tile if the associated grid is not rotated. |
| 37 | + nx: int |
| 38 | + The number of cells in x direction, starting from the ``start_id`` |
| 39 | + ny: int |
| 40 | + The number of cells in y direction, starting from the ``start_id`` |
| 41 | +
|
| 42 | +
|
| 43 | + """ |
| 44 | + |
| 45 | + def __init__( |
| 46 | + self, |
| 47 | + grid: BaseGrid, |
| 48 | + start_id: Union[Tuple[int, int], GridIndex], |
| 49 | + nx: int, |
| 50 | + ny: int, |
| 51 | + ): |
| 52 | + |
| 53 | + if not numpy.isclose(nx % 1, 0): |
| 54 | + raise ValueError(f"Expected an integer for 'nx', got: {nx}") |
| 55 | + if nx < 1: |
| 56 | + raise ValueError(f"Expected 'nx' to be 1 or larger, got: {nx}") |
| 57 | + if not numpy.isclose(ny % 1, 0): |
| 58 | + raise ValueError(f"Expected an integer for 'ny', got: {ny}") |
| 59 | + if ny < 1: |
| 60 | + raise ValueError(f"Expected 'nx' to be 1 or larger, got: {ny}") |
| 61 | + if not len(GridIndex(start_id)) == 1: |
| 62 | + raise ValueError( |
| 63 | + "'start_id' must be a single pair of indices in the form (x,y), got: {start_id}" |
| 64 | + ) |
| 65 | + start_id = ( |
| 66 | + tuple(start_id.index) |
| 67 | + if isinstance(start_id, GridIndex) |
| 68 | + else tuple(start_id) |
| 69 | + ) |
| 70 | + |
| 71 | + if isinstance(grid, TriGrid): |
| 72 | + self._tile = PyO3TriTile(grid._grid, start_id, nx, ny) |
| 73 | + elif isinstance(grid, RectGrid): |
| 74 | + self._tile = PyO3RectTile(grid._grid, start_id, nx, ny) |
| 75 | + elif isinstance(grid, HexGrid): |
| 76 | + self._tile = PyO3HexTile(grid._grid, start_id, nx, ny) |
| 77 | + else: |
| 78 | + raise TypeError( |
| 79 | + f"Unexpected type for 'grid', expected a TriGrid, RectGrid or HexGrid, got a: {type(grid)}" |
| 80 | + ) |
| 81 | + self.grid = grid.update() |
| 82 | + |
| 83 | + @property |
| 84 | + def start_id(self): |
| 85 | + """The starting cell of the Tile. |
| 86 | + The starting cell defines the bottom-left corner of the Tile if the associated grid is not rotated. |
| 87 | + """ |
| 88 | + return GridIndex(self._tile.start_id) |
| 89 | + |
| 90 | + @property |
| 91 | + def nx(self): |
| 92 | + """The number of cells in x direction, starting from the ``start_id``""" |
| 93 | + return self._tile.nx |
| 94 | + |
| 95 | + @property |
| 96 | + def ny(self): |
| 97 | + """The number of cells in y direction, starting from the ``start_id``""" |
| 98 | + return self._tile.ny |
| 99 | + |
| 100 | + def corner_ids(self): |
| 101 | + """The ids at the corners of the Tile |
| 102 | +
|
| 103 | + Returns |
| 104 | + ------- |
| 105 | + :class:`.GridIndex` |
| 106 | + The :class:`.GridIndex` that contains the ids of the cells at |
| 107 | + the corners of the Tile in order: top-left, top-right, bottom-right, bottom-left |
| 108 | + (assuming the assicaited grid is not rotated) |
| 109 | + """ |
| 110 | + return GridIndex(self._tile.corner_ids()) |
| 111 | + |
| 112 | + def corners(self): |
| 113 | + """The coordinates at the corners of the Tile |
| 114 | +
|
| 115 | + Returns |
| 116 | + ------- |
| 117 | + `numpy.ndarray` |
| 118 | + A two-dimensional array that contais the x and y coordinates of |
| 119 | + the corners in order: top-left, top-right, bottom-right, bottom-left |
| 120 | + (assuming the assicaited grid is not rotated) |
| 121 | + """ |
| 122 | + return self._tile.corners() |
| 123 | + |
| 124 | + @property |
| 125 | + def indices(self): |
| 126 | + """The ids of all cells in the Tile. |
| 127 | +
|
| 128 | + Returns |
| 129 | + ------- |
| 130 | + :class:`.GridIndex` |
| 131 | + The :class:`.GridIndex` that contains the indices in the Tile |
| 132 | + """ |
| 133 | + return GridIndex(self._tile.indices()) |
| 134 | + |
| 135 | + @property |
| 136 | + def bounds(self) -> Tuple[float, float, float, float]: |
| 137 | + """The bounding box of the Tile in (xmin, ymin, xmax, ymax). |
| 138 | + If the associated grid is rotated, the this represents the bounding box |
| 139 | + that fully encapsulates the Tile and will contain more area than is |
| 140 | + covered by the rotated Tile. |
| 141 | +
|
| 142 | + Returns |
| 143 | + ------- |
| 144 | + Tuple[float, float, float, float] |
| 145 | + The bounding box in (xmin, ymin, xmax, ymax) |
| 146 | + """ |
| 147 | + return self._tile.bounds() |
0 commit comments