The cell Module¶
Fig. 15 The four ellipsoidal cell shapes (each drawn filled, with its
neighborhood for context). Cells on the equatorial faces O–R are
quads: genuine longitude-latitude rectangles. Cells on the polar
faces N and S are caps (the cell containing the pole), darts
(three-cornered cells) or skew quads (four-cornered cells whose
edges are not meridians and parallels). A cell’s shape is reported by
rhealpixdggs.cell.Cell.ellipsoidal_shape.¶
Fig. 16 The spatial predicates on the planar grid, illustrated with the
children of cell P4: touches is true for neighbors that share an
edge and for cells that meet at a corner only; within /
contains_cell relate a cell to its ancestors and descendants;
disjoint is everything else. Those three relations exhaust the
possibilities for two cells of one hierarchy, so the DE-9IM
overlaps and crosses predicates never hold between cells, and
intersects is the negation of disjoint.¶
Fig. 17 The same predicates among polar cell shapes. The cap N4 touches its edge-neighbor skew quads (here N1) and also touches the darts (here N0), which meet it at a single point (marked). Caps nest like any other cells: N44 is within N4. At a cube corner (right) only three cells meet, and every pair of them shares an edge – unlike a planar grid corner, no pair touches at a corner only.¶
Fig. 18 A corner of the underlying cube, on the ellipsoid. Away from the
cube’s corners every cell corner is shared by four cells, but at the
eight cube corners exactly three cells meet, so one diagonal
neighbor does not exist: here N0.neighbor('up') is Q2 and
N0.neighbor('left') is R0, but N0.diagonal_neighbor('up_left')
is None.¶
Fig. 19 The same corner drawn on the cube itself. The three shaded cells N0, Q2 and R0 each contribute one corner to the shared vertex, and each pair of them shares an edge along one of the three creases – there is simply no fourth, diagonally-opposite cell.¶
|
Represents a cell of the planar or ellipsoidal rHEALPix grid hierarchies. |
- class rhealpixdggs.cell.Cell(rdggs: RHEALPixDGGS, suid: list[str | int] | tuple[str | int, ...] | None = None, level_order_index: int | None = None, post_order_index: int | None = None)[source]¶
Bases:
objectRepresents a cell of the planar or ellipsoidal rHEALPix grid hierarchies. Cell identifiers are of the form (p_0, p_1,…,p_l), where p_0 is one of the characters ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’ and p_i for i > 0 is one of the integers 0, 1,…, N_side**2 - 1, where N_side is the instance attribute from RHEALPixDGGS (the number of children cells along a cell’s side).
INSTANCE ATTRIBUTES:
rdggs - The DGGS that the cell comes from.
ellipsoid - The underlying ellipsoid of the DGGS.
N_side - The N_side attribute of the DGGS
suid - The cell’s ID (tuple). SUID = spatially unique identifier. (‘id’ is a reserved word in Python)
resolution - The cell’s resolution (nonnegative integer).
NOTE:
Several Cell methods have the keyword argument ‘plane’. Setting it to True indicates that all input and output points and cells are to be interpreted as lying in the planar DGGS. Setting it to False indicates that they are to be interpreted as lying in the ellipsoidal DGGS.
- boundary(n: int = 2, plane: bool = True, interior: bool = False) list[tuple[float, float]][source]¶
Return a list of 4*n - 4 boundary points of this cell, n on each edge, where n >= 2. List the points in clockwise order starting from the cell’s upper left corner if plane = True, or from the cell’s northwest corner if plane = False.
If n = 2, then the output is the same as vertices(). If interior = True, then push the boundary points slighly into the interior of the cell, which is convenient for some graphics methods. When plane = False, a point on the antimeridian takes the sign that keeps the ring’s longitude span under half a turn: +180 for a cell just west of the antimeridian, -180 for one just east. Cells that straddle it, and cap cells, keep both signs.
When plane = False, the cost scales with n because each point requires an inverse projection call, except on quad cells. A quad cell lies entirely in the equatorial region, where the inverse projection is separable: longitude depends only on x and latitude only on y. Its east and west edges are meridians sharing the same n - 2 interior latitudes, and its north and south edges are parallels sharing the same n - 2 interior longitudes. Only the four corners and the interior points of the west and north edges are projected (2*n calls instead of 4*n - 4); the east and south edges reuse those values. Every coordinate returned is one the projection computed, and adjacent quad cells get bit-identical shared points, except that an edge on the antimeridian is +180 in the western cell’s ring and -180 in the eastern’s (see
boundary()).For quad and cap cells with n = 2 and interior = False the result is
vertices(plane=False). Cap cells with n > 2 take the general per-point path: a cap’s boundary is a single parallel and could be computed directly too, but there are only two cap cells per resolution, so it isn’t worth a third code path.EXAMPLES:
>>> from rhealpixdggs.utils import my_round >>> from rhealpixdggs.dggs import UNIT_003 >>> rdggs = UNIT_003 >>> c = rdggs.cell(['N', 6]) >>> c.boundary(n=2, plane=True) == c.vertices(plane=True) True >>> for p in c.boundary(n=3, plane=True): ... print(my_round(p, 14)) (-3.14159265358979, 1.30899693899575) (-2.87979326579064, 1.30899693899575) (-2.61799387799149, 1.30899693899575) (-2.61799387799149, 1.0471975511966) (-2.61799387799149, 0.78539816339745) (-2.87979326579064, 0.78539816339745) (-3.14159265358979, 0.78539816339745) (-3.14159265358979, 1.0471975511966) >>> for p in c.boundary(n=3, plane=False): ... print(tuple(x.tolist() for x in my_round(p, 14))) (-180.0, 74.35752898700072) (-157.50000000000003, 58.41366190347208) (-150.0, 41.8103148957786) (-165.00000000000003, 41.8103148957786) (-180.0, 41.8103148957786) (165.0, 41.8103148957786) (149.99999999999997, 41.8103148957786) (157.49999999999997, 58.41366190347208)
- buffer(dist: float, plane: bool = True, n: int = 8) ZoneSet[source]¶
The
bufferof Table 53: seeZoneSet.buffer.
- centroid(plane: bool = True) tuple[float, float][source]¶
Return the centroid of this planar or ellipsoidal cell: the cell’s direct position, in the sense of OGC Topic 21 v2.0 requirement 27 (the DirectPosition of an equal-area cell is its centroid, the geodesic centre of surface area). In the plane it is the centre of the square, the nucleus. On the ellipsoid it is the area-weighted mean of longitude and latitude over the cell, integrated in the equal-area plane, where equal planar area is equal surface area: it lies inside the cell but, except for cap cells and on the equator, not at the nucleus. nucleus() remains the indexing point.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> c = Cell(rdggs, ['P', 0, 2]) >>> centroid = c.centroid() >>> nucleus = c.nucleus() >>> print(centroid == nucleus) True
- child(levels: int = 1, inherit_id: bool = False) ZoneSet[source]¶
The
childof Table 53: seeZoneSet.child.
- child_of(other: Cell, inherit_id: bool = False) bool[source]¶
Return True if this cell is a child of other, one level down:
childOfof Table 53, the converse of parent_of().EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['P', 4, 0]).child_of(Cell(rdggs, ['P', 4])) True
- color(saturation: float = 0.5) tuple[float, float, float][source]¶
Return a unique RGB color tuple for this cell. Inessential graphics method.
- contains(p: tuple[float, float], plane: bool = True) bool[source]¶
Return True if this cell contains point p, and return False otherwise.
EXAMPLES:
>>> from numpy import pi >>> from rhealpixdggs.dggs import WGS84_003_RADIANS >>> rdggs = WGS84_003_RADIANS >>> p = (pi/4, 0) >>> c = rdggs.cell_from_point(2, p, plane=False) >>> print(c) Q44 >>> print(c.contains(p, plane=False)) True
- contains_cell(other: Cell) bool[source]¶
DE-9IM-style contains predicate for a pair of cells: return True if other is this cell or a descendant of it, and False otherwise.
Named contains_cell rather than contains to avoid confusion with the pre-existing contains() method, which tests whether this cell contains a point, not another cell.
Note this coincides with covers(): because cells form a strict hierarchical partition, a descendant cell’s boundary that touches its ancestor’s boundary is still entirely contained in the ancestor’s closed region, so there’s no DE-9IM-style distinction here between “contains” (usually excludes boundary-touching) and “covers” (usually includes it) the way there can be for general geometries.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N']).contains_cell(Cell(rdggs, ['N', 0])) True >>> Cell(rdggs, ['N', 0]).contains_cell(Cell(rdggs, ['N'])) False
- covered_by(other: Cell) bool[source]¶
DE-9IM coveredBy predicate. See within()’s docstring for why this is the same relation as within() for cells.
- covers(other: Cell) bool[source]¶
DE-9IM covers predicate. See contains_cell()’s docstring for why this is the same relation as contains_cell() for cells.
- crosses(other: Cell) bool[source]¶
DE-9IM crosses predicate: return True if the interiors of this cell and other intersect in something of lower dimension than at least one of them, which needs inputs of different dimension (or two lines). Two cells are regions of the same dimension, so this is False for every pair; it exists because OGC Topic 21 mandates the predicate, and it validates its inputs like the other predicates.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N']).crosses(Cell(rdggs, ['N', 0])) False
- diagonal_neighbor(direction: str) Cell | None[source]¶
Return this cell’s diagonal (corner-touching only, not sharing an edge) planar neighbor in the given direction, one of ‘up_left’, ‘up_right’, ‘down_left’, ‘down_right’.
Return None if this cell sits at a genuine cube corner in that direction (i.e. all of its ancestors, down to and including its resolution 0 cell, are positioned exactly in that corner). Cube corners are 3-valent – exactly 3 cells meet there, not 4 – so there is no diagonal 4th cell distinct from the two already reachable via neighbor().
Unlike neighbor(), this has no plane parameter: cell adjacency is a property of the grid’s topology, independent of whether it’s expressed in planar or ellipsoidal coordinates, so this is always computed in the (topologically primary) planar grid.
Note this is not simply self.neighbor(d1, plane=True).neighbor(d2, plane=True) (nor the same composed in the other order): composing two independently rotation-corrected edge steps does not reliably commute near a face boundary, since crossing one face in the first step can change what “the other direction” even means for the second step. This computes both directions’ digit-carrying in a single pass, matching how neighbor() itself only ever applies one rotation correction, and applies that same correction once, for whichever single direction (if any) actually crosses a face.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['N', 4, 0]) >>> print(c.diagonal_neighbor('up_left')) N08
- difference(other: Cell | ZoneSet, min_res: int | None = None, max_res: int | None = None) ZoneSet[source]¶
The
differenceof Table 53: seeZoneSet.difference.
- disjoint(other: Cell) bool[source]¶
DE-9IM disjoint predicate: return True if this cell and other share no point at all (no shared interior and no shared boundary), and False otherwise.
Two cells of one hierarchical grid always either nest (one contains the other), touch along their boundary only, or are disjoint; see overlaps() for why partial overlap is impossible.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N', 0]).disjoint(Cell(rdggs, ['S', 0])) True >>> Cell(rdggs, ['N', 0]).disjoint(Cell(rdggs, ['N', 1])) False
- distance(other: Cell, plane: bool = True, n: int = 8) float[source]¶
Return the distance between this cell and other: the infimum of the distance between a point of one and a point of the other. It is 0 when the cells nest or touch, and otherwise the least distance between their boundaries. With plane = True that is the Euclidean distance in the rHEALPix plane; with plane = False it is the geodesic (shortest surface) distance on the ellipsoid, from
pyproj.Geod. Both are in the ellipsoid’s length units: metres on the WGS84 grids, and on the unit sphere the central angle in radians.The plane is one unfolding of the cube, so plane = True can put cells that are near each other on the ellipsoid far apart: cells close to the seam at x = +-pi R from opposite sides, or a polar cell and an equatorial face its polar square is not attached to in the layout. (Cells that touch across such a seam still give 0, since touching is decided from the grid’s topology.) Use plane = False for a surface distance.
On the ellipsoid each cell’s boundary is sampled at n points per edge, the nearest pair of samples is found, and the result is refined by minimising the geodesic distance along the nearest pair of edges, which are smooth curves, so the answer is accurate to the optimiser’s tolerance (millimetres on WGS84) rather than to the sample spacing. This is the
distanceoperation of OGC Topic 21 v2.0 Table 53; itsprojectToargument selects a dimension, and here the distance is taken in the two surface dimensions.EXAMPLES:
>>> from rhealpixdggs.dggs import UNIT_003 >>> from rhealpixdggs.utils import my_round >>> rdggs = UNIT_003 >>> my_round(Cell(rdggs, ['P', 0]).distance(Cell(rdggs, ['P', 2])), 9) 0.523598776 >>> Cell(rdggs, ['P', 0]).distance(Cell(rdggs, ['P', 1])) 0.0 >>> my_round(Cell(rdggs, ['N']).distance(Cell(rdggs, ['S']), plane=False), 9) 1.459455312
- property ellipsoidal_shape: str¶
Return the shape of this cell (‘quad’, ‘cap’, ‘dart’, or ‘skew_quad’) when viewed on the ellipsoid.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> print(Cell(rdggs, ['P', 2]).ellipsoidal_shape) quad >>> print(Cell(rdggs, ['N', 2]).ellipsoidal_shape) dart
- equals(other: Cell) bool[source]¶
DE-9IM equals predicate: return True if this cell and other are the same cell, and False otherwise. For two cells of one grid this is self == other; unlike ==, which is False across grids, it raises ValueError for cells of different grids or for the empty cell, as every predicate here does.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N', 0]).equals(Cell(rdggs, ['N', 0])) True >>> Cell(rdggs, ['N', 0]).equals(Cell(rdggs, ['N', 1])) False
- index(order: str = 'resolution') int | None[source]¶
Return the index of self when it’s ordered according to order. Here order can be ‘resolution’ (default) or ‘post’. Indices start at 0. The empty cell has index None.
The ordering comes from the way of traversing the tree T of all cells defined as follows. The root of T is a non-cell place holder. The children of the root are the cells A < B < … < F. The children of a cell in T with suid s are s0 < s1 < … < sn, where n = self.N_side**2.
The level order index of a nonempty cell is its position (starting from 0) in the level order traversal of T starting at cell A.
The post order index of a nonempty cell is its position (starting from 0) in the post order traversal of T.
EXAMPLES:
>>> from rhealpixdggs.dggs import UNIT_003 >>> rdggs = UNIT_003 >>> c = Cell(rdggs, ['N', 2]) >>> print(c.index(order='resolution')) 8 >>> print(c.index(order='post')) 2
- interior(n: int = 2, plane: bool = True, flatten: bool = False) list[tuple[float, float]] | list[list[tuple[float, float]]][source]¶
Return an n x n matrix of interior points of this cell. If the cell is planar, space the interior points on a regular square grid. List the points in standard, row-major matrix order. If the cell is ellipsoidal, project the matrix of points to the ellipsoid (longitude-latitude points). If flatten = True, then flatten the matrix into a one dimensional array of pairs.
EXAMPLES:
>>> from rhealpixdggs.utils import my_round >>> from rhealpixdggs.dggs import UNIT_003 >>> rdggs = UNIT_003 >>> c = rdggs.cell(['N']) >>> for p in c.interior(n=2, plane=False, flatten=True): ... print(tuple(x.tolist() for x in my_round(p, 14))) (90.0, 41.8103801453539) (-180.0, 41.8103801453539) (-1e-14, 41.8103801453539) (-90.0, 41.8103801453539) >>> all([c.contains(p) for p in c.interior(n=5, plane=True, flatten=True)]) True
- intersection(other: Cell | ZoneSet, min_res: int | None = None, max_res: int | None = None) ZoneSet[source]¶
The
intersectionof Table 53: seeZoneSet.intersection.
- intersects(other: Cell) bool[source]¶
DE-9IM intersects predicate: return True if this cell and other share at least one point, which for cells means they nest or touch, and False otherwise. The negation of disjoint().
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N', 0]).intersects(Cell(rdggs, ['N', 1])) True >>> Cell(rdggs, ['N']).intersects(Cell(rdggs, ['N', 0])) True >>> Cell(rdggs, ['N', 0]).intersects(Cell(rdggs, ['S', 0])) False
- intersects_meridian(lam: float) bool[source]¶
Return True if this ellipsoidal cell’s boundary intersects the meridian of longitude lam, and return False otherwise.
EXAMPLES:
>>> from numpy import pi >>> from rhealpixdggs.dggs import WGS84_003_RADIANS >>> rdggs = WGS84_003_RADIANS >>> c = rdggs.cell(['N', 6]) >>> print(c.intersects_meridian(-pi)) True >>> print(c.intersects_meridian(-pi/2)) False
- intersects_parallel(phi: float) bool[source]¶
Return True if this cell’s boundary intersects the parallel of latitude phi, and return False otherwise.
- neighbor(direction: str, plane: bool = True) Cell | None[source]¶
Return this cell’s (edge) neighbor in the given direction. If plane = True, then the direction is one of the strings ‘up’, ‘right’, ‘down’, ‘left’, which indicates the desired neighbor relative to x-y coordinates in the following planar neighbor diagram, (drawn for self.N_side = 3) where self is the middle cell
up *-----* | | | | | | *-----*-----*-----* | | 012 | | left | | 345 | | right | | 678 | | *-----*-----*-----* | | | | | | *-----* down
If plane = False, then the direction is relative to longitude-latitude coordinates and is one of the strings ‘west’, ‘east’, ‘north’, ‘south’ for a quad or skew quad cell; ‘west’, ‘east’, ‘southwest’, ‘southeast’ for a northern dart cell; ‘west’, ‘east’, ‘northwest’, ‘northeast’ for a southern dart cell; ‘south_0’, ‘south_1’, ‘south_2’, ‘south_3’ for a northern cap cell; ‘north_0’, ‘north_1’, ‘north_2’, ‘north_3’ for a southern cap cell; For a cap cell, neighbor directions are numbered in increasing longitude, so that the longitude of the (nucleus of) north_0 is less than the longitude of north_1 is less than the longitude of north_2 is less than the longitude of north_3, and the longitude of the south_0 is less than the longitude of south_1, etc.
The tricky part in the planar scenario is that the neighbor relationships of the six resolution 0 cells is determined by the positions of those cells on the surface of a cube, one cell on each face, and not on a plane. So sometimes rotating cells is needed to compute neighbors.
Return None if the given direction is invalid for this cell.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['N', 0]) >>> print(c.neighbor('down')) N3
- neighbors(plane: bool = True) dict[str, Cell][source]¶
Return this cell’s planar or ellipsoidal (edge) neighbors as a dictionary whose keys are the directions of the neighbors. See neighbor() for a list of valid directions.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['N', 0]) >>> for k, v in sorted(c.neighbors().items()): ... print(k, v) ... down N3 left R0 right N1 up Q2
- nucleus(plane: bool = True) tuple[float, float][source]¶
Return the nucleus of this cell: the centre of its planar square, or with plane = False the projection of that point onto the ellipsoid. The nucleus is the grid’s indexing point: it lies inside the cell by construction,
cell_from_pointmaps it back to the cell, and the nuclei of a resolution lie on rings of constant latitude, one perring()number (see Isolatitude Rings). It is not the cell’s published position: while the nucleus of a planar cell is its centroid, the nucleus of an ellipsoidal cell is not, and the direct (representative) position of a cell in the OGC Topic 21 sense is centroid().EXAMPLES:
>>> from rhealpixdggs.utils import my_round >>> from rhealpixdggs.dggs import UNIT_003 >>> rdggs = UNIT_003 >>> c = rdggs.cell(['N']) >>> print(tuple(x.tolist() for x in my_round(c.nucleus(), 14))) (-2.35619449019234, 1.5707963267949)
- nw_vertex(plane: bool = True) tuple[float, float][source]¶
If plane = False, then return the northwest vertex of this ellipsoidal cell. If plane = True, then return the projection onto the plane of the ellipsoidal northwest vertex. On quad cells and cap cells, this function returns the same output as ul_vertex(). On skew quad cells and dart cells, this function returns output different from ul_vertex().
WARNING: The northwest vertex of a cell might not lie in the cell, because not all cells contain their boundary.
- EXAMPLES::
>>> from rhealpixdggs.utils import my_round >>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> c = rdggs.cell(['P', 5, 7]) # Quad cell. >>> print(tuple(x.tolist() for x in my_round(c.ul_vertex(plane=True), 14))) (-2223901.0395045895, -555975.2598761478) >>> print(tuple(x.tolist() for x in my_round(c.nw_vertex(plane=True), 14))) (-2223901.0395045895, -555975.2598761478)
>>> c = rdggs.cell(['S', 4]) # Cap cell. >>> print(tuple(x.tolist() for x in my_round(c.ul_vertex(plane=True), 14))) (-16679257.796284417, -8339628.898142208) >>> print(tuple(x.tolist() for x in my_round(c.nw_vertex(plane=True), 14))) (-16679257.796284417, -8339628.898142208)
>>> c = rdggs.cell(['N', 4, 3]) # Skew quad cell. >>> print(tuple(x.tolist() for x in my_round(c.ul_vertex(plane=True), 14))) (-16679257.796284417, 10563529.937646799) >>> print(tuple(x.tolist() for x in my_round(c.nw_vertex(plane=True), 14))) (-15567307.276532121, 10563529.937646799)
>>> c = rdggs.cell(['S', 4, 3]) # Skew quad cell. >>> print(tuple(x.tolist() for x in my_round(c.ul_vertex(plane=True), 14))) (-16679257.796284417, -9451579.417894501) >>> print(tuple(x.tolist() for x in my_round(c.nw_vertex(plane=True), 14))) (-16679257.796284417, -10563529.937646795)
>>> c = rdggs.cell(['N', 6, 2]) # Dart cell. >>> print(tuple(x.tolist() for x in my_round(c.ul_vertex(plane=True), 14))) (-17791208.316036712, 8339628.898142208) >>> print(tuple(x.tolist() for x in my_round(c.nw_vertex(plane=True), 14))) (-16679257.796284417, 8339628.898142208)
>>> c = rdggs.cell(['S', 6, 2]) # Dart cell. >>> print(tuple(x.tolist() for x in my_round(c.ul_vertex(plane=True), 14))) (-17791208.316036712, -11675480.457399093) >>> print(tuple(x.tolist() for x in my_round(c.nw_vertex(plane=True), 14))) (-16679257.796284417, -12787430.977151388)
- overlaps(other: Cell) bool[source]¶
DE-9IM overlaps predicate: return True if the interiors of this cell and other intersect and neither contains the other. Two cells of one hierarchical grid always either nest, touch along their boundary only, or are disjoint, so this is False for every pair of cells; it exists because OGC Topic 21 mandates the predicate under this name, and it raises ValueError for cells of different grids or the empty cell like the other predicates.
Before 0.9.0 this method answered containment in either direction (one suid a prefix of the other’s). For that relation use
a.contains_cell(b) or a.within(b).Changed in version 0.9.0: DE-9IM semantics; formerly containment in either direction.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N']).overlaps(Cell(rdggs, ['N', 0])) False >>> Cell(rdggs, ['N']).contains_cell(Cell(rdggs, ['N', 0])) True
- parent(levels: int = 1, inherit_id: bool = False) ZoneSet[source]¶
The
parentof Table 53: seeZoneSet.parent.
- parent_of(other: Cell, inherit_id: bool = False) bool[source]¶
Return True if this cell is the parent of other, one level up:
parentOfof OGC Topic 21 v2.0 Table 53. inherit_id is accepted for the spec’s signature; in rHEALPix’s nested hierarchy a child always inherits its parent’s identifier prefix, so it changes nothing.EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['P', 4]).parent_of(Cell(rdggs, ['P', 4, 0])) True >>> Cell(rdggs, ['P', 4]).parent_of(Cell(rdggs, ['P', 4, 0, 0])) False
- predecessor(resolution: int | None = None) Cell | None[source]¶
Return the greatest resolution resolution cell less than self. Note: self need not be a resolution resolution cell.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ('N', 0, 8)) >>> print(c.predecessor()) N07 >>> print(c.predecessor(0)) None >>> print(c.predecessor(1)) None >>> print(c.predecessor(3)) N088
- random_point(plane: bool = True) tuple[float, float][source]¶
Return a random point in this cell. If plane = True, then choose the point from the planar cell. Otherwise, choose the point from the ellipsoidal cell.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['N', 0]) >>> print(c.random_point(plane=False)) (1.4840291937583836, 0.90042819146088571)
- region() str[source]¶
Return the region of this cell: ‘equatorial’, ‘north_polar’, or ‘south_polar’.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> print(Cell(rdggs, ['P', 2]).region()) equatorial >>> print(Cell(rdggs, ['N', 2]).region()) north_polar
- region_overlaps(region: list[Cell]) bool[source]¶
DE-9IM overlaps predicate between this cell and the union of the cells in region: return True if they share interior points but neither covers the other, and False otherwise (including for an empty list). That happens exactly when region holds descendants of this cell that do not tile it completely, and also at least one cell outside it. A region containing this cell or an ancestor, or whose cells inside this cell tile it, covers the cell (within); a region entirely inside this cell is covered by it (contains); a region with no descendant of this cell shares no interior with it. Raises ValueError if any cell belongs to a different grid or is empty.
With a the cell P0 and region given as index strings:
region
result
why
[S8, P03]
True
partial fill, and S8
[P0]
False
covers a exactly
[P03]
False
a covers it
the nine children of P0, and S8
False
the children tile a
[P1, S8]
False
no shared interior
Changed in version 0.9.0: DE-9IM semantics; formerly True if any cell of region contained, or was contained by, this cell.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> a = Cell(rdggs, ['P', 0]) >>> a.region_overlaps([Cell(rdggs, ['S', 8]), Cell(rdggs, ['P', 0, 3])]) True >>> a.region_overlaps([Cell(rdggs, ['P', 0, 3])]) False >>> a.region_overlaps(list(a.subcells()) + [Cell(rdggs, ['S', 8])]) False
- relate(other: Cell, matrix: str) bool[source]¶
DE-9IM relate predicate: return True if the DE-9IM matrix of this cell against other matches the nine-character pattern matrix, read row by row as this cell’s interior, boundary and exterior against other’s. Pattern characters are
T(a non-empty intersection of any dimension),F(empty),*(anything) and0,1,2(that dimension exactly). Therelateoperation of OGC Topic 21 v2.0 Table 53.Between two cells of one hierarchy only six matrices occur: equal cells
2FFF1FFF2; an ancestor against a descendant212F11FF2when the descendant touches the ancestor’s boundary and212FF1FF2when it is interior, and their transposes for a descendant against its ancestor; cells touching along an edgeFF2F11212, at a cornerFF2F01212; and disjoint cellsFF2FF1212.EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> p0, p1 = Cell(rdggs, ['P', 0]), Cell(rdggs, ['P', 1]) >>> p0.relate(p1, 'FF*F1****') # touching along an edge True >>> p0.relate(p1, 'T********') # interiors intersect False >>> Cell(rdggs, ['P']).relate(p0, 'T*****FF*') # contains True
- relate_position(other: Cell, relate: RelativePosition, direction: tuple[float, float] = (1.0, 0.0)) bool[source]¶
Return True if relative_position(other, direction) is relate, or belongs to it when relate is one of the groupings
RelativePosition.IN(STARTS, DURING or FINISHES) andRelativePosition.DISJOINT(BEFORE or AFTER). TherelatePositionoperation of OGC Topic 21 v2.0 Table 53.EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> p1 = Cell(rdggs, ['P', 1]) >>> p1.relate_position(Cell(rdggs, ['P']), RelativePosition.IN) True >>> p1.relate_position(Cell(rdggs, ['P', 0]), RelativePosition.DISJOINT) False
- relative_position(other: Cell, direction: tuple[float, float] = (1.0, 0.0)) RelativePosition[source]¶
Return the position of this cell relative to other along direction, as a RelativePosition: project both planar cells onto the line through the origin with that direction, giving each a closed interval from its smallest to its largest coordinate, and classify the two intervals. This is the
relativePositionoperation of OGC Topic 21 v2.0 Table 53; direction is the spatial part of itsprojectTovector, which for a two-dimensional grid can only have those two components nonzero.The default direction (1, 0) is the planar x axis, west to east in the equatorial belt; (0, 1) is the planar y axis, south to north there. Polar cells’ orientation depends on
north_squareandsouth_square, and the plane is one unfolding of the cube, so cells that meet across the seam at x = +-pi R are far apart along x. Along either axis two cells of one hierarchy only ever give EQUALS, the nesting relations, MEETS/MET_BY or BEFORE/AFTER, because their edges line up; OVERLAPS needs a diagonal direction. Raise ValueError for a zero vector.EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> p0, p1 = Cell(rdggs, ['P', 0]), Cell(rdggs, ['P', 1]) >>> p0.relative_position(p1).name 'MEETS' >>> p1.relative_position(Cell(rdggs, ['P'])).name 'DURING' >>> p0.relative_position(p1, direction=(1, 1)).name 'OVERLAPS' >>> Cell(rdggs, ['P', 3]).relative_position(p0, direction=(0, 1)).name 'MEETS'
- ring() int[source]¶
Return the isolatitude ring of this cell’s nucleus. Rings are numbered 0 at the north pole through
n + 2 * q - 1at the south pole, wheren = N_side ** resolutioncells span a base cell’s side andq = ceil(n / 2)rings fill each polar cap. Every cell on a ring, in any base cell, has the same nucleus latitude. Ring numbers are only comparable within one resolution. See Isolatitude Rings.EXAMPLES:
>>> from rhealpixdggs.dggs import WGS84_003 >>> suids = [['N', 4], ['N', 0], ['P', 4], ['S', 8]] >>> [Cell(WGS84_003, s).ring() for s in suids] [0, 1, 3, 5]
- rotate(quarter_turns: int) Cell[source]¶
Return the cell that is the result of rotating this cell’s resolution 0 supercell by quarter_turns quarter turns anticlockwise. Used in neighbor().
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['N', 0]) >>> print([str(c.rotate(t)) for t in range(4)]) ['N0', 'N2', 'N8', 'N6']
- rotate_entry(x: str | int, quarter_turns: int) str | int[source]¶
Let N = self.N_side and rotate the N x N matrix of subcell numbers
0 1 ... N - 1 N N+1 ... 2*N - 1 ... (N-1)*N (N-1)*N+1 ... N**2-1
anticlockwise by quarter_turns quarter turns to obtain a new table with entries f(0), f(1), …, f(N**2 - 1) read from left to right and top to bottom. Given entry number x in the original matrix, return f(x). Used in rotate().
INPUT:
x - A letter from CELLS0 or one of the integers 0, 1, …, N**2 - 1.
quarter_turns - 0, 1, 2, or 3.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['P', 2]) >>> print([c.rotate_entry(0, t) for t in range(4)]) [0, 2, 8, 6]
NOTES:
Operates on letters from CELLS0 too. They stay fixed under f. Only depends on self through self.N_side.
- sibling(levels: int = 1, inherit_id: bool = False) ZoneSet[source]¶
The
siblingof Table 53: seeZoneSet.sibling.
- sibling_of(other: Cell, inherit_id: bool = False) bool[source]¶
Return True if other is a sibling of this cell, itself included:
siblingOfof Table 53. With inherit_id True siblings share a parent; with inherit_id False (the default) inheritance is ignored and siblings are the cells at the same resolution that share an edge or a corner, so the neighbours across a parent’s edge count too, as the spec’s example40.siblingOf(31)shows.EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> p40, p32 = Cell(rdggs, ['P', 4, 0]), Cell(rdggs, ['P', 3, 2]) >>> p40.sibling_of(Cell(rdggs, ['P', 4, 1])) True >>> p40.sibling_of(p32), p40.sibling_of(p32, inherit_id=True) (True, False)
- subcell(other: Cell) bool[source]¶
Subcell (subset) relation on cells.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> a = Cell(RHEALPixDGGS(), ('N', 1)) >>> b = Cell(RHEALPixDGGS(), ['N']) >>> print(a.subcell(b)) True >>> print(b.subcell(a)) False
- subcells(resolution: int | None = None) Iterator[Cell][source]¶
Generator function for the set of all resolution resolution subcells of this cell. If resolution=None, then return a generator function for the children of this cell.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ['N']) >>> print([str(cell) for cell in c.subcells()]) ['N0', 'N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'N8']
- successor(resolution: int | None = None) Cell | None[source]¶
Return the least resolution resolution cell greater than self. Note: self need not be a resolution resolution cell.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> c = Cell(RHEALPixDGGS(), ('N', 8, 2)) >>> print(c.successor()) N83 >>> print(c.successor(0)) O >>> print(c.successor(1)) O0 >>> print(c.successor(3)) N830
- static suid_from_index(rdggs: RHEALPixDGGS, index: int, order: str = 'resolution') tuple[str | int, ...][source]¶
Return the suid of a cell from its index. The index is according to the cell ordering order, which can be ‘resolution’ (default) or ‘post’. See the index() docstring for more details on orderings. For internal use.
- suid_rowcol() tuple[tuple[str | int, ...], tuple[str | int, ...]][source]¶
Return the pair of row- and column-suids of self, each as tuples.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> c = Cell(rdggs, ['N', 7, 3]) >>> rsuid, csuid = c.suid_rowcol() >>> print(rsuid == ('N', 2, 1)) True >>> print(csuid == ('N', 1, 0)) True
- sym_difference(other: Cell | ZoneSet, min_res: int | None = None, max_res: int | None = None) ZoneSet[source]¶
The
symDifferenceof Table 53: seeZoneSet.sym_difference.
- touches(other: Cell) bool[source]¶
DE-9IM touches predicate: return True if this cell and other share at least one boundary point but neither contains the other (equivalently, no ancestor/descendant relationship and no shared interior), and False otherwise. self and other may be of different resolutions.
Two cells of the same resolution touch exactly when one is an edge or diagonal (corner-only) neighbor of the other – see neighbor() and diagonal_neighbor().
For cells of different resolutions (informally, “cousins” – neither an ancestor of the other, but nested in siblings that are themselves edge/diagonal neighbors, possibly several levels up), this finds their two ancestors at the shallower of the two resolutions and checks those for edge/diagonal adjacency. If they’re edge-adjacent, the deeper cell touches the shallower one exactly when every one of the deeper cell’s digits below that ancestor lies in the row or column bordering the shared edge (so the deeper cell never strays from that edge); if they’re diagonally adjacent, the same must hold for the single digit value that is that shared corner, since a corner is a single point rather than a whole edge. Since the shallower cell exposes its entire edge or corner at that resolution, nothing else needs checking on its side.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N', 0]).touches(Cell(rdggs, ['N', 1])) True >>> Cell(rdggs, ['O']).touches(Cell(rdggs, ['P', 3])) True >>> Cell(rdggs, ['N', 0]).touches(Cell(rdggs, ['N', 8])) False
- ul_vertex(plane: bool = True) tuple[float, float][source]¶
If plane = True, then return the upper left vertex of this planar cell. If plane = False, then return the projection onto the ellipsoid of the planar upper left vertex. Note that for polar cells, this projection is not necessarily the northwest vertex. For the latter vertex use nw_vertex().
WARNING: The upper left vertex of a cell might not lie in the cell, because not all cells contain their boundary.
EXAMPLES:
>>> from numpy import pi >>> from rhealpixdggs.dggs import UNIT_003 >>> c = Cell(UNIT_003, ['N', 0]) >>> print(c.ul_vertex() == (-pi, 3*pi/4)) True
- union(other: Cell | ZoneSet, min_res: int | None = None, max_res: int | None = None) ZoneSet[source]¶
The
unionof Table 53: seeZoneSet.union.
- vertices(plane: bool = True, trim_dart: bool = False) list[tuple[float, float]][source]¶
If plane = True, then assume this cell is planar and return its four vertices in the order (upper left corner, upper right corner, lower right corner, lower left corner). If plane = False, then assume this cell is ellipsoidal and return the projection of the planar vertices in the order (northwest, northeast, southeast, southwest). If plane = False, this cell is a dart cell, and trim_dart = True, then remove the one non-vertex point from the output. (Dart cells only have three vertices.) A vertex on the antimeridian takes the sign that keeps the cell’s longitude span under half a turn, as
boundary()describes.EXAMPLES:
>>> from rhealpixdggs.utils import my_round >>> from rhealpixdggs.dggs import UNIT_003, WGS84_003 >>> rdggs = UNIT_003 >>> c = rdggs.cell(['N']) >>> for p in c.vertices(): ... print(tuple(x.tolist() for x in my_round(p, 14))) (-3.14159265358979, 2.35619449019234) (-1.5707963267949, 2.35619449019234) (-1.5707963267949, 0.78539816339745) (-3.14159265358979, 0.78539816339745) >>> rdggs = WGS84_003 >>> c = rdggs.cell(['N', 0]) >>> for p in c.vertices(plane=False): ... print(tuple(x.tolist() for x in my_round(p, 14))) (89.99999999999993, 74.42400670199599) (119.99999999999999, 41.93785391016014) (90.0, 41.93785391016014) (59.99999999999999, 41.93785391016014) >>> for p in c.vertices(plane=False, trim_dart=True): ... print(tuple(x.tolist() for x in my_round(p, 14))) (89.99999999999993, 74.42400670199599) (119.99999999999999, 41.93785391016014) (59.99999999999999, 41.93785391016014) >>> c = rdggs.cell(['S', 0]) >>> for p in c.vertices(plane=False): ... print(tuple(x.tolist() for x in my_round(p, 13))) (150.0, -41.9378539101601) (-180.0, -41.9378539101601) (-150.0, -41.9378539101601) (-179.9999999999999, -74.424006701996) >>> for p in c.vertices(plane=False, trim_dart=True): ... print(tuple(x.tolist() for x in my_round(p, 13))) (150.0, -41.9378539101601) (-150.0, -41.9378539101601) (-179.9999999999999, -74.424006701996)
- width(plane: Literal[True] = True) float[source]¶
- width(plane: Literal[False]) None
Return the width of this cell. If plane = False, then return None, because ellipsoidal cells don’t have a fixed width.
EXAMPLES:
>>> from numpy import pi >>> from rhealpixdggs.dggs import UNIT_003 >>> c = Cell(UNIT_003, ('N', 8)) >>> print(c) N8 >>> c.width() == pi/2*3**(-1) True
- within(other: Cell) bool[source]¶
DE-9IM within predicate: return True if this cell is other or a descendant of it, and False otherwise. The converse of contains_cell(); see that method’s docstring for why this also coincides with covered_by() for cells.
EXAMPLES:
>>> from rhealpixdggs.dggs import RHEALPixDGGS >>> rdggs = RHEALPixDGGS() >>> Cell(rdggs, ['N', 0]).within(Cell(rdggs, ['N'])) True
- within_distance(other: Cell, dist: float, plane: bool = True, n: int = 8) bool[source]¶
Return True if distance(other, plane, n) is less than dist, and False otherwise: the
withinDistanceoperation of OGC Topic 21 v2.0 Table 53, with its strict inequality. See distance() for the definition and units.EXAMPLES:
>>> from rhealpixdggs.dggs import UNIT_003 >>> rdggs = UNIT_003 >>> Cell(rdggs, ['P', 0]).within_distance(Cell(rdggs, ['P', 2]), 0.6) True >>> Cell(rdggs, ['P', 0]).within_distance(Cell(rdggs, ['P', 2]), 0.5) False
- xy_range() tuple[tuple[float, float], tuple[float, float]][source]¶
Return the x- and y-coordinate extremes of the planar version of this cell in the format ((x_min, x_max), (y_min, y_max)).
EXAMPLES:
>>> from numpy import pi >>> from rhealpixdggs.dggs import UNIT_003 >>> rdggs = UNIT_003 >>> c = rdggs.cell(['N']) >>> c.xy_range() == ((-pi, -pi/2), (pi/4, 3*pi/4)) True
- class rhealpixdggs.cell.RelativePosition(*values)[source]¶
Bases:
EnumThe relative position of two cells projected onto one direction, as
Cell.relative_positionreports it: the enumeration of OGC Topic 21 v2.0 Table 54, whose names follow OGC 16-071r3 (OWL-Time). Each cell projects to a closed interval from begin to end along the direction, and the thirteen primary members are the interval relations of Allen (1983), here with self the cell asked and other the argument:member
condition
BEFORE
self.end < other.begin
AFTER
self.begin > other.end
MEETS
self.end == other.begin
MET_BY
self.begin == other.end
OVERLAPS
self.begin < other.begin < self.end < other.end
OVERLAPPED_BY
other.begin < self.begin < other.end < self.end
STARTS
self.begin == other.begin and self.end < other.end
STARTED_BY
self.begin == other.begin and self.end > other.end
DURING
other.begin < self.begin and self.end < other.end
CONTAINS
self.begin < other.begin and other.end < self.end
FINISHES
self.end == other.end and self.begin > other.begin
FINISHED_BY
self.end == other.end and self.begin < other.begin
EQUALS
self.begin == other.begin and self.end == other.end
(Table 54 prints FINISHED_BY with its begin inequality reversed and OVERLAPPED_BY without its begin clause; these are the standard relations.) The table’s two further members are groupings: IN is STARTS, DURING or FINISHES, and DISJOINT is BEFORE or AFTER.
relative_positionnever returns them;relate_positionaccepts them, as do theis_inandis_disjointproperties.Read along the planar x axis the words describe east-west arrangement in the equatorial belt: BEFORE is “entirely west of”, MEETS “shares an edge on the east side”, DURING “lies within the other’s east-west span”. Along planar y they describe south to north.
- AFTER = 'After'¶
- BEFORE = 'Before'¶
- CONTAINS = 'Contains'¶
- DISJOINT = 'Disjoint'¶
- DURING = 'During'¶
- EQUALS = 'Equals'¶
- FINISHED_BY = 'FinishedBy'¶
- FINISHES = 'Finishes'¶
- IN = 'In'¶
- MEETS = 'Meets'¶
- MET_BY = 'MetBy'¶
- OVERLAPPED_BY = 'OverlappedBy'¶
- OVERLAPS = 'Overlaps'¶
- STARTED_BY = 'StartedBy'¶
- STARTS = 'Starts'¶