diff --git a/setup.cfg b/setup.cfg index b4e3d46..bccf21e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = ozon3 -version = 1.3.1 +version = 1.4.0 author = Milind Sharma author_email = milindsharma8@gmail.com description = A package to get air quality data using the WAQI API diff --git a/setup.py b/setup.py index 99c6c2a..d913acc 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ description="A package to get air quality data using the WAQI API", license="GPLv3+", url="https://github.com/Milind220/Ozone", - version="1.3.1", - download_url="https://github.com/Milind220/Ozone/archive/refs/tags/v1.3.1.tar.gz", + version="1.4.0", + download_url="https://github.com/Milind220/Ozone/archive/refs/tags/v1.4.0.tar.gz", packages=setuptools.find_packages(), install_requires=[ "numpy; python_version>='3'", diff --git a/src/ozone/ozone.py b/src/ozone/ozone.py index f886c24..8be99b7 100644 --- a/src/ozone/ozone.py +++ b/src/ozone/ozone.py @@ -15,6 +15,7 @@ import numpy import requests import json +import itertools from ratelimit import limits, sleep_and_retry from .urls import URLs @@ -218,6 +219,40 @@ def _AQI_meaning(self, aqi: float) -> Tuple[str, str]: return AQI_meaning, AQI_health_implications + def _locate_all_coordinates( + self, lower_bound: Tuple[float, float], upper_bound: Tuple[float, float] + ) -> List[Tuple]: + """Get all locations between two pair of coordinates + + Args: + lower_bound (tuple): start location + upper_bound (tuple): end location + + Returns: + list: a list of all coordinates located between lower_bound and + upper_bound. If API request fails then returns [(-1, -1)]. + """ + + coordinates_flattened: List[float] = list( + itertools.chain(lower_bound, upper_bound) + ) + latlng: str = ",".join(map(str, coordinates_flattened)) + response = self._make_api_request( + f"{URLs.find_coordinates_url}bounds/?token={self.token}&latlng={latlng}" + ) + if self._check_status_code(response): + data = json.loads(response.content)["data"] + coordinates: List[Tuple] = [ + (element["lat"], element["lon"]) for element in data + ] + return coordinates + + # This is a bit of a hack to ensure that the function always returns a + # list of coordinates. Required to make mypy happy. + + # Return an invalid coordinate if API request fails. + return [(-1, -1)] + def get_coordinate_air( self, lat: float, @@ -314,6 +349,35 @@ def get_multiple_coordinate_air( df.reset_index(inplace=True, drop=True) return self._format_output(data_format, df) + def get_range_coordinates_air( + self, + lower_bound: Tuple[float, float], + upper_bound: Tuple[float, float], + data_format: str = "df", + df: pandas.DataFrame = pandas.DataFrame(), + params: List[str] = [""], + ) -> pandas.DataFrame: + """Get air quality data for range of coordinates between lower_bound and upper_bound + + Args: + lower_bound (tuple): start coordinate + upper_bound (tuple): end coordinate + data_format (str): File format. Defaults to 'df'. Choose from 'csv', 'json', 'xslx'. + df (pandas.DataFrame, optional): An existing dataframe to append the data to. + params (List[str], optional): A list of parameters to get data for. + Gets all parameters by default. + + Returns: + pandas.DataFrame: The dataframe containing the data. (If you + selected another data format, this dataframe will be empty) + """ + locations = self._locate_all_coordinates( + lower_bound=lower_bound, upper_bound=upper_bound + ) + return self.get_multiple_coordinate_air( + locations, data_format=data_format, df=df, params=params + ) + def get_multiple_city_air( self, cities: List[str], diff --git a/src/ozone/urls.py b/src/ozone/urls.py index 77cfd85..d7ce8d1 100644 --- a/src/ozone/urls.py +++ b/src/ozone/urls.py @@ -32,6 +32,9 @@ class URLs: # For search for air quality measuring stations in area. find_stations_url: str = f"{_base_url}search/" + # For Map Queries + find_coordinates_url: str = f"{_base_url}map/" + if __name__ == "__main__": pass