Skip to content

Commit

Permalink
Improve google reverse geocoding parse results
Browse files Browse the repository at this point in the history
  • Loading branch information
ysavary committed Dec 2, 2023
1 parent cdba823 commit 007c190
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 102 deletions.
95 changes: 47 additions & 48 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions providers/metar.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ def process_data(self):
measures_collection = self.measures_collection(station_id)
if not self.has_measure(measures_collection, key):
try:
if (
not metar.xpath("wind_dir_degrees")
and not metar.xpath("wind_speed_kt")
and not metar.xpath("wind_gust_kt")
):
raise ProviderException("No wind data")

wind_dir_attr = get_attr(metar, "wind_dir_degrees")
if wind_dir_attr == "VRB":
# For VaRiaBle direction, use a random value
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ timezonefinder = "6.2.0"
black = "23.11.0"
flake8 = "6.1.0"
isort = "5.12.0"
pytest = "7.1.3"
python-dotenv = "^0.21.0"
pytest = "7.4.3"
pytest-dotenv = "0.5.2"
python-dotenv = "1.0.0"

[tool.pytest.ini_options]
env_files = ".env.localhost"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
20 changes: 20 additions & 0 deletions tests/provider_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from unittest import mock

import pytest
import requests

from winds_mobi_provider import Provider


@pytest.mark.skip("Need a redis connection to Google API caches")
@pytest.mark.parametrize(
"station_id,expected_name",
[("metar-LSMP", "Payerne"), ("metar-LSGC", "Aero Group S.A."), ("metar-LSGG", "Geneva Airport")],
)
@mock.patch("winds_mobi_provider.provider.MongoClient")
def test_parse_reverse_geocoding_results(mongodb, station_id, expected_name):
lon, lat = requests.get(f"https://winds.mobi/api/2.3/stations/{station_id}").json()["loc"]["coordinates"]
provider = Provider()
short_name, name = provider._Provider__parse_reverse_geocoding_results(f"address2/{lat},{lon}", None, None, None)
assert short_name == expected_name
assert name == expected_name
115 changes: 63 additions & 52 deletions winds_mobi_provider/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,16 @@ def call_google_api(self, url, api_name):
raise ProviderException(f"[{api_name}] ZERO_RESULTS: url='{url}'")
return result

def __parse_reverse_geocoding_results(self, results):
lookup_types = [
def __parse_reverse_geocoding_results(self, address_key, short_name, name, default_name) -> Tuple[str, str]:
cache = self.redis.hgetall(address_key)
if cache.get("error"):
if default_name:
self.log.warning(f"Unable to determine station names: {cache['error']}")
return short_name or default_name, name or default_name
else:
raise ProviderException(f"Unable to determine station names: {cache['error']}")

address_types = [
"airport",
"locality",
"colloquial_area",
Expand All @@ -168,13 +176,33 @@ def __parse_reverse_geocoding_results(self, results):
"sublocality",
"administrative_area_level_3",
]
for lookup_type in lookup_types:
for result in results:
for component in result["address_components"]:
if lookup_type in component["types"]:
short_name, long_name = component["short_name"], component["long_name"]
return short_name, long_name
return None

def order_by_type(address):
for address_type in address_types:
if address_type in address["types"]:
try:
return address_types.index(address_type)
except ValueError:
pass
return 100

addresses = json.loads(cache["json"])["results"]
addresses.sort(key=order_by_type)

if len(addresses) > 0:
for address_type in address_types:
# Use the first address because they are ordered by importance
for component in addresses[0]["address_components"]:
if address_type in component["types"]:
return (
short_name or component["short_name"] or default_name,
name or component["long_name"] or default_name,
)
if not default_name:
raise ProviderException(f"Google Reverse Geocoding API: no address match for '{address_key}'")

self.log.warning(f"Google Reverse Geocoding API: no address match for '{address_key}'")
return short_name or default_name, name or default_name

def __compute_elevation(self, lat, lon) -> Tuple[float, bool]:
radius = 500
Expand Down Expand Up @@ -264,26 +292,32 @@ def save_station(
if lat < -90 or lat > 90 or lon < -180 or lon > 180:
raise ProviderException(f"Invalid latitude '{lat}' or longitude '{lon}'")

address_key = f"address2/{lat},{lon}"
if (not short_name or not name) and not self.redis.exists(address_key):
try:
result = self.call_google_api(
f"https://maps.googleapis.com/maps/api/geocode/json?latlng={lat},{lon}",
"Google Reverse Geocoding API",
)
self.add_redis_key(
address_key,
{"json": json.dumps(result)},
self.google_api_cache_duration,
)
except TimeoutError as e:
raise e
except UsageLimitException as e:
self.add_redis_key(address_key, {"error": repr(e)}, self.usage_limit_cache_duration)
except Exception as e:
if not isinstance(e, ProviderException):
self.log.exception("Unable to call Google Reverse Geocoding API")
self.add_redis_key(address_key, {"error": repr(e)}, self.google_api_error_cache_duration)
if not short_name or not name:
address_key = f"address2/{lat},{lon}"
if not self.redis.exists(address_key):
try:
result = self.call_google_api(
f"https://maps.googleapis.com/maps/api/geocode/json?latlng={lat},{lon}",
"Google Reverse Geocoding API",
)
self.add_redis_key(
address_key,
{"json": json.dumps(result)},
self.google_api_cache_duration,
)
except TimeoutError as e:
raise e
except UsageLimitException as e:
self.add_redis_key(address_key, {"error": repr(e)}, self.usage_limit_cache_duration)
except Exception as e:
if not isinstance(e, ProviderException):
self.log.exception("Unable to call Google Reverse Geocoding API")
self.add_redis_key(address_key, {"error": repr(e)}, self.google_api_error_cache_duration)

short_name, name = self.__parse_reverse_geocoding_results(address_key, short_name, name, default_name)
if len(short_name) > len(name):
# Swap short_name and name
short_name, name = name, short_name

alt_key = f"alt/{lat},{lon}"
if not self.redis.exists(alt_key):
Expand All @@ -299,29 +333,6 @@ def save_station(
self.log.exception("Unable to call Google Elevation API")
self.add_redis_key(alt_key, {"error": repr(e)}, self.google_api_error_cache_duration)

if not short_name or not name:
if self.redis.hexists(address_key, "error"):
if default_name:
short_name = short_name or default_name
name = name or default_name
self.log.warning(f"Unable to determine station names: {self.redis.hget(address_key, 'error')}")
else:
raise ProviderException(
f"Unable to determine station names: {self.redis.hget(address_key, 'error')}"
)
else:
api_result = json.loads(self.redis.hget(address_key, "json"))
if names := self.__parse_reverse_geocoding_results(api_result["results"]):
address_short_name, address_long_name = names
short_name = short_name or address_short_name
name = name or address_long_name
else:
raise ProviderException(f"Google Reverse Geocoding API: no address match for '{address_key}'")

if len(short_name) > len(name):
# Swap short_name and name
short_name, name = name, short_name

if not altitude:
if self.redis.hexists(alt_key, "error"):
raise ProviderException(
Expand Down

0 comments on commit 007c190

Please sign in to comment.