Skip to content

Commit

Permalink
TYPE: Add type hints (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
snowman2 authored Apr 19, 2022
1 parent 2d0fca1 commit 05d9713
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 90 deletions.
33 changes: 18 additions & 15 deletions gars_field/edgarsgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"""
import math
import re
from typing import Optional

import shapely.geometry

Expand All @@ -84,7 +85,7 @@ class EDGARSGrid(GARSGridBase):
r"(?P<quadrant_1deg>[1-9])?)?$"
)

def __init__(self, gars_id, max_resolution=None):
def __init__(self, gars_id: str, max_resolution: Optional[int] = None) -> None:
"""
Parameters
----------
Expand All @@ -111,13 +112,13 @@ def __init__(self, gars_id, max_resolution=None):
gars_match = self.RE_PATTERN.match(gars_id)
if not gars_match:
raise ValueError(f'"{gars_id}" is not a valid ED-GARS grid ID.')
self.gars_id = gars_id
self.gars_id: str = gars_id

gars_dict = gars_match.groupdict()

self.quadrant_1deg = gars_dict["quadrant_1deg"]
self.quadrant_3deg = gars_dict["quadrant_3deg"]
self.quadrant_6deg = gars_dict["quadrant_6deg"]
self.quadrant_1deg: str = gars_dict["quadrant_1deg"]
self.quadrant_3deg: str = gars_dict["quadrant_3deg"]
self.quadrant_6deg: str = gars_dict["quadrant_6deg"]

lon_num = int(self.quadrant_6deg[:2])
if (lon_num < 1) or (lon_num > 60):
Expand All @@ -133,20 +134,22 @@ def __init__(self, gars_id, max_resolution=None):
)

# determine resolution from ED-GARS ID
self.resolution = 6
self.resolution: int = 6
if self.quadrant_1deg:
self.resolution = 1
elif self.quadrant_3deg:
self.resolution = 3

# properties
self._polygon = None
self._polygon: Optional[shapely.geometry.Polygon] = None

def __repr__(self):
def __repr__(self) -> str:
return f"<ED-GARS(gars_id={self}, resolution={self.resolution})>"

@classmethod
def from_latlon(cls, latitude, longitude, resolution):
def from_latlon(
cls, latitude: float, longitude: float, resolution: int
) -> "EDGARSGrid":
"""Load ED-GARS grid from latitude and longitude.
Parameters
Expand Down Expand Up @@ -186,20 +189,20 @@ def from_latlon(cls, latitude, longitude, resolution):
if resolution < 6:
lon_3deg_idx = math.floor((longitude % 6) / 3.0) + 1
lat_3deg_idx = 2 - math.floor((latitude % 6) / 3.0)
quadrant_3deg = int((lat_3deg_idx - 1) * 2 + lon_3deg_idx)
quadrant_3deg = str(int((lat_3deg_idx - 1) * 2 + lon_3deg_idx))

# 1 deg quadrant
if resolution < 3:
lon_1deg_idx = math.floor(longitude % 3) + 1
lat_1deg_idx = 3 - math.floor(latitude % 3)
quadrant_1deg = int((lat_1deg_idx - 1) * 3 + lon_1deg_idx)
quadrant_1deg = str(int((lat_1deg_idx - 1) * 3 + lon_1deg_idx))

gars_id = "".join(["D", quadrant_6deg, str(quadrant_3deg), str(quadrant_1deg)])
gars_id = "".join(["D", quadrant_6deg, quadrant_3deg, quadrant_1deg])

return cls(gars_id, max_resolution=resolution)

@property
def polygon(self):
def polygon(self) -> shapely.geometry.Polygon:
"""Generates the GARS bounding polygon.
Returns
Expand All @@ -212,15 +215,15 @@ def polygon(self):

# CALCULATE 6 DEG DEGREES
# get 6 DEG quadrant info
longitude = ((int(self.quadrant_6deg[:2]) - 1) * 6) - 180
longitude: float = ((int(self.quadrant_6deg[:2]) - 1) * 6) - 180

# first 6 deg north/south letter, A-B
lat_6deg_letter1 = self.quadrant_6deg[2]

# second 6 deg north/south letter, A-V
lat_6deg_letter2 = self.quadrant_6deg[3]

latitude = (-90.0 + (self.LETTERS.index(lat_6deg_letter1) * 120.0)) + (
latitude: float = (-90.0 + (self.LETTERS.index(lat_6deg_letter1) * 120.0)) + (
self.LETTERS.index(lat_6deg_letter2) * 6.0
)

Expand Down
75 changes: 43 additions & 32 deletions gars_field/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
"""
import itertools
from typing import List, Optional, Tuple, Type

import shapely

from .edgarsgrid import EDGARSGrid
from .garsgrid import GARSGrid
from .garsgrid import GARSGrid, GARSGridBase
from .gedgarsgrid import GEDGARSGrid


Expand All @@ -19,7 +22,7 @@ class GARSField:
"""

# pylint: disable=too-many-instance-attributes
def __init__(self, bounding_geom):
def __init__(self, bounding_geom: shapely.geometry.base.BaseGeometry) -> None:
"""
Parameters
----------
Expand All @@ -29,19 +32,19 @@ def __init__(self, bounding_geom):
"""
self.bounding_geom = bounding_geom

self._gars_60deg = None
self._gars_30deg = None
self._gars_60deg: Optional[List[GEDGARSGrid]] = None
self._gars_30deg: Optional[List[GEDGARSGrid]] = None

self._gars_6deg = None
self._gars_3deg = None
self._gars_1deg = None
self._gars_6deg: Optional[List[EDGARSGrid]] = None
self._gars_3deg: Optional[List[EDGARSGrid]] = None
self._gars_1deg: Optional[List[EDGARSGrid]] = None

self._gars_30min = None
self._gars_15min = None
self._gars_5min = None
self._gars_1min = None
self._gars_30min: Optional[List[GARSGrid]] = None
self._gars_15min: Optional[List[GARSGrid]] = None
self._gars_5min: Optional[List[GARSGrid]] = None
self._gars_1min: Optional[List[GARSGrid]] = None

def _get_bounds(self):
def _get_bounds(self) -> Tuple[float, float, float, float]:
"""
Retrieve the bounding coordinates of the input geometry
Expand All @@ -60,34 +63,42 @@ def _get_bounds(self):
return min_lon, min_lat, max_lon, max_lat

@staticmethod
def _get_lat_letter_range(gars_grid, ll_lat1, ll_lat2, ur_lat1, ur_lat2):
def _get_lat_letter_range(
gars_grid: Type[GARSGridBase],
ll_lat1: str,
ll_lat2: str,
ur_lat1: str,
ur_lat2: str,
) -> List[str]:
"""
Retrieve the latitude letter range for the bounding box.
"""
# ignoring types due to: https://github.com/python/mypy/issues/4125

# first north/south letter, GARSGrid (A-Q), EDGARSGRid (A-B)
lat_letter1_range = gars_grid.LETTERS[
gars_grid.LETTERS.index(ll_lat1) : gars_grid.LETTERS.index(ur_lat1) + 1
lat_letter1_range: str = gars_grid.LETTERS[ # type: ignore
gars_grid.LETTERS.index(ll_lat1) : gars_grid.LETTERS.index(ur_lat1) + 1 # type: ignore
]

# second north/south letter, A-Z
lat_letter2_range = gars_grid.LETTERS[
gars_grid.LETTERS.index(ll_lat2) : gars_grid.LETTERS.index(ur_lat2) + 1
lat_letter2_range: str = gars_grid.LETTERS[ # type: ignore
gars_grid.LETTERS.index(ll_lat2) : gars_grid.LETTERS.index(ur_lat2) + 1 # type: ignore
]

lat_letter_range = []
lat_letter_range: List[str] = []
if ll_lat1 != ur_lat1:
# part 1: using first letter1 and all from first letter2 to end of all LETTERS
for letter2 in gars_grid.LETTERS[gars_grid.LETTERS.index(ll_lat2) :]:
for letter2 in gars_grid.LETTERS[gars_grid.LETTERS.index(ll_lat2) :]: # type: ignore
lat_letter_range.append("".join([ll_lat1, letter2]))

# part 2: from second letter1 to next to last letter2 with all LETTERS
for letter1, letter2 in itertools.product(
lat_letter1_range[1:-1], gars_grid.LETTERS
lat_letter1_range[1:-1], gars_grid.LETTERS # type: ignore
):
lat_letter_range.append("".join([letter1, letter2]))

# part 3: using last letter1 and all from beginning of all LETTERS to last last letter2
for letter2 in gars_grid.LETTERS[: gars_grid.LETTERS.index(ur_lat2) + 1]:
for letter2 in gars_grid.LETTERS[: gars_grid.LETTERS.index(ur_lat2) + 1]: # type: ignore
lat_letter_range.append("".join([ur_lat1, letter2]))

else:
Expand All @@ -98,7 +109,7 @@ def _get_lat_letter_range(gars_grid, ll_lat1, ll_lat2, ur_lat1, ur_lat2):
return lat_letter_range

@property
def gars_60deg(self):
def gars_60deg(self) -> List[GEDGARSGrid]:
"""list: The 36deg GEDGARSGrid objects that intersect the bounding geometry."""
if self._gars_60deg is not None:
return self._gars_60deg
Expand All @@ -124,7 +135,7 @@ def gars_60deg(self):
return self._gars_60deg

@property
def gars_30deg(self):
def gars_30deg(self) -> List[GEDGARSGrid]:
"""list: The 30deg GEDGARSGrid objects that intersect the bounding geometry."""
if self._gars_30deg is not None:
return self._gars_30deg
Expand All @@ -148,7 +159,7 @@ def gars_30deg(self):
self._gars_30deg.append(g30deg)
return self._gars_30deg

def _get_6deg_ranges(self):
def _get_6deg_ranges(self) -> Tuple[List[str], List[str]]:
"""Retrieves the latitude numeric range and longitude letter range
within the bounds.
"""
Expand All @@ -169,7 +180,7 @@ def _get_6deg_ranges(self):
return lon_num_range, lat_letter_range

@property
def gars_6deg(self):
def gars_6deg(self) -> List[EDGARSGrid]:
"""list: The 6deg EDGARSGrid objects that intersect the bounding geometry."""
if self._gars_6deg is not None:
return self._gars_6deg
Expand Down Expand Up @@ -198,7 +209,7 @@ def gars_6deg(self):
return self._gars_6deg

@property
def gars_3deg(self):
def gars_3deg(self) -> List[EDGARSGrid]:
"""list: The 3deg EDGARSGrid objects that intersect the bounding geometry."""
if self._gars_3deg is not None:
return self._gars_3deg
Expand All @@ -221,7 +232,7 @@ def gars_3deg(self):
return self._gars_3deg

@property
def gars_1deg(self):
def gars_1deg(self) -> List[EDGARSGrid]:
"""list: The 1deg EDGARSGrid objects that intersect the bounding geometry."""
if self._gars_1deg is not None:
return self._gars_1deg
Expand All @@ -243,7 +254,7 @@ def gars_1deg(self):
self._gars_1deg.append(g1deg)
return self._gars_1deg

def _get_30min_ranges(self):
def _get_30min_ranges(self) -> Tuple[List[str], List[str]]:
"""Retrieves the latitude numeric range and longitude letter range
within the bounds.
"""
Expand All @@ -264,7 +275,7 @@ def _get_30min_ranges(self):
return lon_num_range, lat_letter_range

@property
def gars_30min(self):
def gars_30min(self) -> List[GARSGrid]:
"""list: The 30min GARSGrid objects that intersect the bounding geometry."""
if self._gars_30min is not None:
return self._gars_30min
Expand Down Expand Up @@ -293,7 +304,7 @@ def gars_30min(self):
return self._gars_30min

@property
def gars_15min(self):
def gars_15min(self) -> List[GARSGrid]:
"""list: The 15min GARSGrid objects that intersect the bounding geometry."""
if self._gars_15min is not None:
return self._gars_15min
Expand All @@ -318,7 +329,7 @@ def gars_15min(self):
return self._gars_15min

@property
def gars_5min(self):
def gars_5min(self) -> List[GARSGrid]:
"""list: The 5min GARSGrid objects that intersect the bounding geometry."""
if self._gars_5min is not None:
return self._gars_5min
Expand All @@ -343,7 +354,7 @@ def gars_5min(self):
return self._gars_5min

@property
def gars_1min(self):
def gars_1min(self) -> List[GARSGrid]:
"""list: The 1min GARSGrid objects that intersect the bounding geometry."""
if self._gars_1min is not None:
return self._gars_1min
Expand Down
Loading

0 comments on commit 05d9713

Please sign in to comment.