def weather_around_coords(self, lat, lon, limit=None): """ Queries the OWM Weather API for the currently observed weather in all the locations in the proximity of the specified coordinates. :param lat: location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param limit: the maximum number of *Observation* items in the returned list (default is ``None``, which stands for any number of items) :param limit: int or ``None`` :returns: a list of *Observation* objects or ``None`` if no weather data is available :raises: *ParseResponseException* when OWM Weather API responses' data cannot be parsed, *APICallException* when OWM Weather API can not be reached, *ValueError* when coordinates values are out of bounds or negative values are provided for limit """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat} if limit is not None: assert isinstance(limit, int), "'limit' must be an int or None" if limit < 1: raise ValueError("'limit' must be None or greater than zero") params['cnt'] = limit _, json_data = self.http_client.get_json(FIND_OBSERVATIONS_URI, params=params) return observation.Observation.from_dict_of_lists(json_data)
def uvindex_history_around_coords(self, lat, lon, start, end=None): """ Queries for UV index historical values in the surroundings of the provided geocoordinates and in the specified time frame. If the end of the time frame is not provided, that is intended to be the current datetime. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param start: the object conveying the time value for the start query boundary :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: the object conveying the time value for the end query boundary (defaults to ``None``, in which case the current datetime will be used) :type end: int, ``datetime.datetime`` or ISO8601-formatted string :return: a list of *UVIndex* instances or empty list if data is not available :raises: *ParseResponseException* when OWM UV Index API responses' data cannot be parsed, *APICallException* when OWM UV Index API can not be reached, *ValueError* for wrong input values """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) assert start is not None start = formatting.timeformat(start, 'unix') if end is None: end = timestamps.now(timeformat='unix') else: end = formatting.timeformat(end, 'unix') params = {'lon': lon, 'lat': lat, 'start': start, 'end': end} json_data = self.uv_client.get_uvi_history(params) uvindex_list = [uvindex.UVIndex.from_dict(item) for item in json_data] return uvindex_list
def one_call(self, lat: Union[int, float], lon: Union[int, float]) -> one_call.OneCall: """ Queries the OWM Weather API with one call for current weather information and forecast for the specified geographic coordinates. One Call API provides the following weather data for any geographical coordinate: - Current weather - Hourly forecast for 48 hours - Daily forecast for 7 days A *OneCall* object is returned with the current data and the two forecasts. :param lat: location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: location's longitude, must be between -180.0 and 180.0 :type lon: int/float :returns: a *OneCall* instance or ``None`` if the data is not available for the specified location :raises: *ParseResponseException* when OWM Weather API responses' data cannot be parsed, *APICallException* when OWM Weather API can not be reached """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat} _, json_data = self.http_client.get_json(ONE_CALL_URI, params=params) return one_call.OneCall.from_dict(json_data)
def __init__(self, lat: Union[int, float], lon: Union[int, float], timezone: str, current: Weather, forecast_minutely: Optional[Weather] = None, forecast_hourly: Optional[Weather] = None, forecast_daily: Optional[Weather] = None, national_weather_alerts: Optional[list] = None ) -> None: geo.assert_is_lat(lat) self.lat = lat geo.assert_is_lon(lon) self.lon = lon self.timezone = timezone if current is None: raise ValueError("'current' must be set") self.current = current self.forecast_minutely = forecast_minutely self.forecast_hourly = forecast_hourly self.forecast_daily = forecast_daily self.national_weather_alerts = national_weather_alerts
def weather_at_places_in_bbox(self, lon_left, lat_bottom, lon_right, lat_top, zoom=10, cluster=False): """ Queries the OWM Weather API for the weather currently observed by meteostations inside the bounding box of latitude/longitude coords. :param lat_top: latitude for top margin of bounding box, must be between -90.0 and 90.0 :type lat_top: int/float :param lon_left: longitude for left margin of bounding box must be between -180.0 and 180.0 :type lon_left: int/float :param lat_bottom: latitude for the bottom margin of bounding box, must be between -90.0 and 90.0 :type lat_bottom: int/float :param lon_right: longitude for the right margin of bounding box, must be between -180.0 and 180.0 :type lon_right: int/float :param zoom: zoom level (defaults to: 10) :type zoom: int :param cluster: use server clustering of points :type cluster: bool :returns: a list of *Observation* objects or ``None`` if no weather data is available :raises: *ParseResponseException* when OWM Weather API responses' data cannot be parsed, *APICallException* when OWM Weather API can not be reached, *ValueError* when coordinates values are out of bounds or negative values are provided for limit """ geo.assert_is_lon(lon_left) geo.assert_is_lon(lon_right) geo.assert_is_lat(lat_bottom) geo.assert_is_lat(lat_top) assert type(zoom) is int, "'zoom' must be an int" if zoom <= 0: raise ValueError("'zoom' must greater than zero") assert type(cluster) is bool, "'cluster' must be a bool" params = { 'bbox': ','.join([ str(lon_left), str(lat_bottom), str(lon_right), str(lat_top), str(zoom) ]), 'cluster': 'yes' if cluster else 'no' } _, json_data = self.http_client.get_json(BBOX_CITY_URI, params=params) return observation.Observation.from_dict_of_lists(json_data)
def __init__(self, name, lon, lat, ID, country=None): self._name = name if lon is None or lat is None: raise ValueError("Either 'lon' or 'lat' must be specified") geo.assert_is_lon(lon) geo.assert_is_lat(lat) self._lon = float(lon) self._lat = float(lat) self._ID = ID self._country = country
def reverse_geocode(self, lat, lon, limit=None): geo.assert_is_lon(lon) geo.assert_is_lat(lat) if limit is not None: assert isinstance(limit, int) assert limit > 0 params = {'lat': lat, 'lon': lon} if limit is not None: params['limit'] = limit _, json_data = self.http_client.get_json(REVERSE_GEOCODING_URI, params=params) return [Location.from_dict(item) for item in json_data]
def forecast_at_coords(self, lat, lon, interval, limit=None): """ Queries the OWM Weather API for weather forecast for the specified geographic coordinates with the given time granularity. A *Forecaster* object is returned, containing a *Forecast*: this instance encapsulates *Weather* objects corresponding to the provided granularity. :param lat: location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param interval: the granularity of the forecast, among `3h` and 'daily' :type interval: str among `3h` and 'daily' :param limit: the maximum number of *Weather* items to be retrieved (default is ``None``, which stands for any number of items) :type limit: int or ``None`` :returns: a *Forecaster* instance or ``None`` if forecast data is not available for the specified location :raises: *ParseResponseException* when OWM Weather API responses' data cannot be parsed, *APICallException* when OWM Weather API can not be reached """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) assert isinstance(interval, str), "Interval must be a string" if limit is not None: assert isinstance(limit, int), "'limit' must be an int or None" if limit < 1: raise ValueError("'limit' must be None or greater than zero") params = {'lon': lon, 'lat': lat} if limit is not None: params['cnt'] = limit if interval == '3h': uri = THREE_HOURS_FORECAST_URI elif interval == 'daily': uri = DAILY_FORECAST_URI else: raise ValueError("Unsupported time interval for forecast") _, json_data = self.http_client.get_json(uri, params=params) fc = forecast.Forecast.from_dict(json_data) if fc is not None: fc.interval = interval return forecaster.Forecaster(fc) else: return None
def uvindex_forecast_around_coords(self, lat, lon): """ Queries for forecast Ultra Violet values in the next 8 days in the surroundings of the provided geocoordinates. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :return: a list of *UVIndex* instances or empty list if data is not available :raises: *ParseResponseException* when OWM UV Index API responses' data cannot be parsed, *APICallException* when OWM UV Index API can not be reached, *ValueError* for wrong input values """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat} json_data = self.uv_client.get_uvi_forecast(params) return [uvindex.UVIndex.from_dict(item) for item in json_data]
def uvindex_around_coords(self, lat, lon): """ Queries for Ultra Violet value sampled in the surroundings of the provided geocoordinates and in the specified time interval. A *UVIndex* object instance is returned, encapsulating a *Location* object and the UV intensity value. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :return: a *UVIndex* instance or ``None`` if data is not available :raises: *ParseResponseException* when OWM UV Index API responses' data cannot be parsed, *APICallException* when OWM UV Index API can not be reached, *ValueError* for wrong input values """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat} json_data = self.uv_client.get_uvi(params) return uvindex.UVIndex.from_dict(json_data)
def weather_at_coords(self, lat, lon): """ Queries the OWM Weather API for the currently observed weather at the specified geographic (eg: 51.503614, -0.107331). :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :returns: an *Observation* instance or ``None`` if no weather data is available :raises: *ParseResponseException* when OWM Weather API responses' data cannot be parsed or *APICallException* when OWM Weather API can not be reached """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat} _, json_data = self.http_client.get_json(OBSERVATION_URI, params=params) return observation.Observation.from_dict(json_data)
def air_quality_forecast_at_coords(self, lat, lon): """ Queries the OWM AirPollution API for available forecasted air quality indicators around the specified coordinates. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :return: a `list` of *AirStatus* instances or an empty `list` if data is not available :raises: *ParseResponseException* when OWM AirPollution API responses' data cannot be parsed, *APICallException* when OWM AirPollution API can not be reached, *ValueError* for wrong input values """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat} json_data = self.new_ap_client.get_forecast_air_pollution(params) try: return airstatus.AirStatus.from_dict(json_data) except: return []
def one_call_history(self, lat: Union[int, float], lon: Union[int, float], dt: int = None): """ Queries the OWM Weather API with one call for historical weather information for the specified geographic coordinates. A *OneCall* object is returned with the current data and the two forecasts. :param lat: location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param dt: timestamp from when the historical data starts. Cannot be less then now - 5 days. Default = None means now - 5 days :type dt: int :returns: a *OneCall* instance or ``None`` if the data is not available for the specified location :raises: *ParseResponseException* when OWM Weather API responses' data cannot be parsed, *APICallException* when OWM Weather API can not be reached """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) if dt is None: dt = int( (datetime.now() - timedelta(days=5)).replace(tzinfo=timezone.utc).timestamp()) else: if not isinstance(dt, int): raise ValueError("dt must be of type int") if dt < 0: raise ValueError("dt must be positive") params = {'lon': lon, 'lat': lat, 'dt': dt} _, json_data = self.http_client.get_json(ONE_CALL_HISTORICAL_URI, params=params) return one_call.OneCall.from_dict(json_data)
def test_assert_is_lon(self): self.assertRaises(AssertionError, geo.assert_is_lon, '23.7') self.assertRaises(ValueError, geo.assert_is_lon, -200.0) self.assertRaises(ValueError, geo.assert_is_lon, 200.0) geo.assert_is_lon(180) geo.assert_is_lon(-180) geo.assert_is_lon(-45.6)
def coindex_around_coords(self, lat, lon, start=None, interval=None): """ Queries the OWM AirPollution API for Carbon Monoxide values sampled in the surroundings of the provided geocoordinates and in the specified time interval. A *COIndex* object instance is returned, encapsulating a *Location* object and the list of CO samples If `start` is not provided, the latest available CO samples are retrieved If `start` is provided but `interval` is not, then `interval` defaults to the maximum extent, which is: `year` :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param start: the object conveying the start value of the search time window start (defaults to ``None``). If not provided, the latest available CO samples value are retrieved :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param interval: the length of the search time window starting at `start` (defaults to ``None``). If not provided, 'year' is used :type interval: str among: 'minute', 'hour', 'day', 'month, 'year' :return: a *COIndex* instance or ``None`` if data is not available :raises: *ParseResponseException* when OWM AirPollution API responses' data cannot be parsed, *APICallException* when OWM AirPollution API can not be reached, *ValueError* for wrong input values """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) params = {'lon': lon, 'lat': lat, 'start': start, 'interval': interval} json_data = self.ap_client.get_coi(params) coi = coindex.COIndex.from_dict(json_data) if interval is None: interval = 'year' coi.interval = interval return coi
def air_quality_history_at_coords(self, lat, lon, start, end=None): """ Queries the OWM AirPollution API for available forecasted air quality indicators around the specified coordinates. :param lat: the location's latitude, must be between -90.0 and 90.0 :type lat: int/float :param lon: the location's longitude, must be between -180.0 and 180.0 :type lon: int/float :param start: the object conveying the start value of the search time window :type start: int, ``datetime.datetime`` or ISO8601-formatted string :param end: the object conveying the end value of the search time window. Values in the future will be clipped to the current timestamp. Defaults to the current UNIX timestamp. :type end: int, ``datetime.datetime`` or ISO8601-formatted string :return: a `list` of *AirStatus* instances or an empty `list` if data is not available :raises: *ParseResponseException* when OWM AirPollution API responses' data cannot be parsed, *APICallException* when OWM AirPollution API can not be reached, *ValueError* for wrong input values """ geo.assert_is_lon(lon) geo.assert_is_lat(lat) now = timestamps.now(timeformat='unix') assert start is not None start = formatting.timeformat(start, 'unix') if end is None: end = now else: end = formatting.timeformat(end, 'unix') if end > now: end = now params = {'lon': lon, 'lat': lat, 'start': start, 'end': end} json_data = self.new_ap_client.get_historical_air_pollution(params) try: return airstatus.AirStatus.from_dict(json_data) except: return []