Spaces:
Runtime error
Runtime error
| import requests | |
| import pandas as pd | |
| import datetime | |
| import time | |
| import gradio as gr | |
| import os | |
| ########### | |
| # other API's of interest: https://medium.com/@imdipto/best-free-alternatives-to-the-wunderground-weather-api-21acb22450e6 | |
| ########## | |
| OPENWEATHER_API_KEY = os.environ.get('OPENWEATHER_API_KEY') | |
| WEATHERAPI_KEY = os.environ.get('WEATHERAPI_KEY') | |
| def openweather_to_result(lat, lon, gmt_time): | |
| """ | |
| API docs: https://openweathermap.org/api/one-call-api#current | |
| Parameters | |
| ------------ | |
| lat [float]: decimal valued latitude | |
| lon [float]: decimal valued longitude | |
| gmt_time [datetime object]: time of desired forecast, in gmt and as python datetime object | |
| Returns | |
| -------- | |
| cloud_pct Tuple(List, List): list of cloud percent and corresponding time for times within 1.5 hours of input GMT time | |
| """ | |
| exclude_parts = 'current,minutely,daily,alerts' | |
| request_url = f'https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude={exclude_parts}&appid={OPENWEATHER_API_KEY}' | |
| response = requests.get(request_url) | |
| data = response.json() | |
| cloud_pct = [] | |
| forecast_times = [] | |
| # timeframe around input time to check cloud % for | |
| timeframe = datetime.timedelta(hours=1, minutes=30) | |
| for hour in data['hourly']: | |
| # dt property is unix utc time of forecasted data - convert this to python datetime object | |
| forecast_time = datetime.datetime.fromtimestamp( | |
| hour['dt'], tz=datetime.timezone.utc) | |
| if abs(forecast_time - gmt_time) <= timeframe: | |
| # cloud pct is stored in each hour at top level | |
| cloud_pct.append(hour['clouds']) | |
| forecast_times.append(forecast_time) | |
| return cloud_pct, forecast_times | |
| def weatherapi_to_result(lat, lon, gmt_time): | |
| """ | |
| API docs: https://www.weatherapi.com/docs/ | |
| TODO: implement wrapper instead https://github.com/weatherapicom/weatherapi-Python | |
| Parameters | |
| ------------ | |
| lat [float]: decimal valued latitude | |
| lon [float]: decimal values longitude | |
| gmt_time [datetime object]: time of desired forecast, in gmt and as python datetime object | |
| Returns | |
| -------- | |
| cloud_pct Tuple(List, List): list of cloud percent and corresponding time for times within 1.5 hours of input GMT time | |
| """ | |
| request_url = f'http://api.weatherapi.com/v1/forecast.json?key={WEATHERAPI_KEY}&q={lat},{lon}&days=2&alerts=no' | |
| response = requests.get(request_url) | |
| data = response.json() | |
| timezone = data['location']['tz_id'] | |
| cloud_pct = [] | |
| forecast_times = [] | |
| # quick error handling to make sure input time python object has "timezone" property attached | |
| try: | |
| gmt_time = gmt_time.astimezone(datetime.timezone.utc) | |
| except: | |
| gmt_time = gmt_time.tz_localize('utc') | |
| # timeframe around input time to check cloud % for | |
| timeframe = datetime.timedelta(hours=1, minutes=30) | |
| # this api is first divided into days, then hours | |
| for day in data['forecast']['forecastday']: | |
| for hour in day['hour']: | |
| # time_epoch contains unix epoch time in GMT/UTC | |
| #forecast_time = datetime.datetime.fromtimestamp(hour['time_epoch'], ZoneInfo(timezone)) | |
| forecast_time = datetime.datetime.fromtimestamp( | |
| hour['time_epoch'], datetime.timezone.utc) | |
| if abs(forecast_time - gmt_time) <= timeframe: | |
| cloud_pct.append(hour['cloud']) | |
| forecast_times.append( | |
| forecast_time.astimezone(datetime.timezone.utc)) | |
| return cloud_pct, forecast_times | |
| def met_to_result(lat, lon, gmt_time): | |
| """ | |
| API doc: https://api.met.no/weatherapi/locationforecast/2.0/documentation | |
| How to: https://api.met.no/doc/locationforecast/HowTO | |
| Parameters | |
| ------------ | |
| lat [float]: decimal valued latitude | |
| lon [float]: decimal values longitude | |
| gmt_time [datetime object]: time of desired forecast, in gmt and as python datetime object | |
| Returns | |
| -------- | |
| cloud_pct Tuple(List, List): list of cloud percent and corresponding time for times within 1.5 hours of input GMT time | |
| """ | |
| # set user agent https://stackoverflow.com/questions/10606133/sending-user-agent-using-requests-library-in-python | |
| # must be unique per API Terms of Service https://api.met.no/doc/TermsOfService | |
| headers = { | |
| 'User-Agent': 'NASAEarthScienceRemoteSensingUnit [email protected]'} | |
| request_url = f'https://api.met.no/weatherapi/locationforecast/2.0/compact?lat={lat}&lon={lon}' | |
| response = requests.get(request_url, headers=headers) | |
| data = response.json() | |
| cloud_pct = [] | |
| forecast_times = [] | |
| # timeframe around input time to check cloud % for | |
| timeframe = datetime.timedelta(hours=1, minutes=30) | |
| # walk through json return | |
| for hour in data['properties']['timeseries']: | |
| # time is utc formatted time https://api.met.no/doc/locationforecast/FAQ | |
| forecast_time = datetime.datetime.strptime( | |
| hour['time'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=datetime.timezone.utc) | |
| # check if time of forecast is withing "timeframe" of desired time | |
| if abs(forecast_time - gmt_time) <= timeframe: | |
| # grab cloud pct from location within the nested json, add to list | |
| cloud_pct.append(hour['data']['instant'] | |
| ['details']['cloud_area_fraction']) | |
| # add time of forecast to list. Should be an "on the hour" time | |
| forecast_times.append(forecast_time) | |
| return cloud_pct, forecast_times | |
| ################ | |
| # generate text | |
| ################ | |
| def file_to_cloud_listing(input_file, services): | |
| """ | |
| Args: | |
| input_file (Union[str, gradio FileType]): input csv file with LAT, LON, SITE, GMT cols | |
| services (List): list of weather api servies to check | |
| Returns: | |
| str: formatted string with weather predictions for locations | |
| """ | |
| # this works if the input is from gradio. Then the file has an name property | |
| try: | |
| sites = pd.read_csv(input_file.name, parse_dates=['GMT']) | |
| using_gradio = True | |
| except: | |
| # this is for input from a script or command line | |
| sites = pd.read_csv(input_file, parse_dates=['GMT']) | |
| using_gradio = False | |
| start = time.perf_counter() | |
| date_format = "%H:%M" | |
| text = '' | |
| # each row is a site. Get weather data and then print it for each service for each site. | |
| for row_idx, row in sites.iterrows(): | |
| #time_of_interest = datetime.datetime.strptime(row.GMT, '%m/%d/%y %H:%M') | |
| text += check_row(row, services, date_format) | |
| text += f'{"="*60}\n' | |
| return text | |
| def check_row(row, services, date_format="%H:%M"): | |
| """Check a row of data (a pd.Series with LAT, LON, GMT, SITE cols) | |
| Args: | |
| row (pd.Series): pd.Series with LAT, LON, GMT, SITE cols) | |
| services (List): List of weather services (['OpenWeather', 'MET (Norwegian)', 'WeatherAPI'] or subset) | |
| date_format (str, optional): Format for printing time of site pass over. Defaults to "%H:%M". | |
| Returns: | |
| str: formatted str of text for weather vals | |
| """ | |
| text = "" | |
| text += f'{"Location":13}:\t\t{row.SITE} @ {row["GMT"].strftime(date_format)} GMT\n' | |
| if not isinstance(row.GMT, datetime.datetime): | |
| GMT = row["GMT"].to_pydatetime() | |
| else: | |
| GMT = row["GMT"] | |
| GMT = GMT.replace(tzinfo=datetime.timezone.utc) | |
| if 'OpenWeather' in services: | |
| try: | |
| cldp, times = openweather_to_result(row.LAT, row.LON, GMT) | |
| text += format_cldp_and_time("OpenWeather", cldp=cldp, times=times) | |
| except Exception as e: | |
| text += f'OpenWeather:\t\tError {e} in API processing\n' | |
| if 'MET (Norwegian)' in services: | |
| try: | |
| cldp, times = met_to_result(row.LAT, row.LON, GMT) | |
| text += format_cldp_and_time("Norwegian", cldp=cldp) | |
| except Exception as e: | |
| text += f'Norwegian:\t\tError {e} in API processing\n' | |
| if 'WeatherAPI' in services: | |
| try: | |
| cldp, times = weatherapi_to_result(row.LAT, row.LON, GMT) | |
| text += format_cldp_and_time("WeatherAPI", cldp=cldp) | |
| except Exception as e: | |
| text += f'WeatherAPI:\t\tError {e} in API processing\n' | |
| return text | |
| def format_cldp_and_time(api_name, cldp, times=None): | |
| """Formats output text for lists of cloud percents and forecast times | |
| Args: | |
| api_name ([type]): Name of weather source. | |
| cldp (List): List of floating point cloud percentage values. | |
| times (List, optional): List of forecast times, as datetime objects. Defaults to None. | |
| Returns: | |
| str: formatted text for printing | |
| """ | |
| text = '' | |
| date_format = "%H:%M" | |
| if times is not None: | |
| text += f'{"Forecast Time:":13}\t\t' + ' '.join(time.strftime(date_format) | |
| for time in times) + "\n" | |
| text += f'{api_name:13}:\t\t{" ".join(f"{p:<6.0f}" for p in cldp)}\n' | |
| return text | |
| inputs = [gr.inputs.File(label='Site File with Lat/Lon and GMT Time'), gr.inputs.CheckboxGroup(label='Weather Services', | |
| choices=['OpenWeather', 'MET (Norwegian)', 'WeatherAPI'], default=['OpenWeather', 'MET (Norwegian)'])] | |
| outputs = gr.outputs.Textbox(label ='Cloud % for hour before, hour of, hour after') | |
| css = """* {font-family: "Lucida Console", "Courier New", monospace !important;/* <-- fonts */ | |
| }""" | |
| gr.Interface(fn=file_to_cloud_listing, inputs=inputs, css=css, outputs=outputs, | |
| allow_screenshot=False).launch() | |