Skip to content

Commit 6731479

Browse files
committed
Implemented Geocoding API support - fixes #364
1 parent 715a29c commit 6731479

File tree

17 files changed

+360
-8
lines changed

17 files changed

+360
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ With PyOWM you can integrate into your code any of the following OpenWeatherMap
3232
- **Stations API v3.0**, allowing to create and manage meteostation and publish local weather measurements
3333
- **Weather Alerts API v3.0**, allowing to set triggers on weather conditions and areas and poll for spawned alerts
3434
- **Image tiles** for several map layers provided by OWM
35+
- **Geocoding API v1.0** allowing to perform direct/reverse geocoding
3536

3637
## Get started
3738

pyowm/commons/uris.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
ROOT_GEOCODING_API_URL = 'openweathermap.org/geo/1.0'
5+
DIRECT_GEOCODING_URI = 'direct'
6+
REVERSE_GEOCODING_URI = 'reverse'
7+

pyowm/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
AGRO_API_VERSION = (1, 0, 0)
66
AIRPOLLUTION_API_VERSION = (3, 0, 0)
77
ALERT_API_VERSION = (3, 0, 0)
8+
GEOCODING_API_VERSION = (1, 0, 0)
89
STATIONS_API_VERSION = (3, 0, 0)
910
UVINDEX_API_VERSION = (3, 0, 0)
1011
WEATHER_API_VERSION = (2, 5, 0)

pyowm/geocodingapi10/__init__.py

Whitespace-only changes.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from pyowm.commons.http_client import HttpClient
2+
from pyowm.commons.uris import ROOT_GEOCODING_API_URL, DIRECT_GEOCODING_URI, REVERSE_GEOCODING_URI
3+
from pyowm.constants import GEOCODING_API_VERSION
4+
from pyowm.utils import geo
5+
from pyowm.weatherapi25.location import Location
6+
7+
8+
class GeocodingManager:
9+
10+
"""
11+
A manager objects that provides a full interface to OWM Geocoding API.
12+
13+
:param API_key: the OWM API key
14+
:type API_key: str
15+
:param config: the configuration dictionary
16+
:type config: dict
17+
:returns: an *GeocodingManager* instance
18+
:raises: *AssertionError* when no API Key is provided
19+
20+
"""
21+
22+
def __init__(self, API_key, config):
23+
assert API_key is not None, 'You must provide a valid API Key'
24+
self.API_key = API_key
25+
assert isinstance(config, dict)
26+
self.http_client = HttpClient(API_key, config, ROOT_GEOCODING_API_URL)
27+
28+
def geocoding_api_version(self):
29+
return GEOCODING_API_VERSION
30+
31+
def geocode(self, toponym, country=None, state_code=None, limit=None):
32+
"""
33+
Invokes the direct geocoding API endpoint
34+
35+
:param toponym: the name of the location
36+
:type toponym: `str`
37+
:param country: the 2-chars ISO symbol of the country
38+
:type country: `str` or `None`
39+
:param state_code: the 2-chars ISO symbol of state (only useful in case the country is US)
40+
:type state_code: `str` or `None`
41+
:param limit: the max number of results to be returned in case of multiple matchings (no limits by default)
42+
:type limit: `int` or `None`
43+
:returns: a list of *Location* instances
44+
:raises: *AssertionError*, *ValueError*, *APIRequestError*
45+
46+
"""
47+
assert toponym, 'Toponym must be specified'
48+
if country is not None and len(country) != 2:
49+
raise ValueError("Country must be a 2-char string")
50+
if state_code is not None and len(state_code) != 2:
51+
raise ValueError("State Code must be a 2-char string")
52+
if limit is not None:
53+
assert isinstance(limit, int)
54+
assert limit > 0
55+
56+
query = toponym
57+
if state_code is not None:
58+
query += ',' + state_code
59+
if country is not None:
60+
query += ',' + country
61+
62+
params = {'q': query}
63+
64+
if limit is not None:
65+
params['limit'] = limit
66+
67+
_, json_data = self.http_client.get_json(DIRECT_GEOCODING_URI, params=params)
68+
return [Location.from_dict(item) for item in json_data]
69+
70+
def reverse_geocode(self, lat, lon, limit=None):
71+
geo.assert_is_lon(lon)
72+
geo.assert_is_lat(lat)
73+
if limit is not None:
74+
assert isinstance(limit, int)
75+
assert limit > 0
76+
77+
params = {'lat': lat, 'lon': lon}
78+
if limit is not None:
79+
params['limit'] = limit
80+
81+
_, json_data = self.http_client.get_json(REVERSE_GEOCODING_URI, params=params)
82+
return [Location.from_dict(item) for item in json_data]
83+
84+
def __repr__(self):
85+
return '<%s.%s>' % (__name__, self.__class__.__name__)

pyowm/owm.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pyowm.agroapi10 import agro_manager
66
from pyowm.airpollutionapi30 import airpollution_manager
77
from pyowm.alertapi30 import alert_manager
8+
from pyowm.geocodingapi10 import geocoding_manager
89
from pyowm.stationsapi30 import stations_manager
910
from pyowm.tiles import tile_manager
1011
from pyowm.utils import strings
@@ -125,6 +126,14 @@ def weather_manager(self):
125126
"""
126127
return weather_manager.WeatherManager(self.api_key, self.config)
127128

129+
def geocoding_manager(self):
130+
"""
131+
Gives a `pyowm.geocoding10.geocoding_manager.GeocodingManager` instance that can be used to perform direct
132+
and reverse geocoding
133+
:return: a `pyowm.geocoding10.geocoding_manager.GeocodingManager` instance
134+
"""
135+
return geocoding_manager.GeocodingManager(self.api_key, self.config)
136+
128137
def __repr__(self):
129138
return "<%s.%s - API key=%s, subscription type=%s, PyOWM version=%s>" % \
130139
(__name__,

pyowm/weatherapi25/location.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ def from_dict(cls, the_dict):
8383
else:
8484
lon = 0.0
8585
lat = data['station']['coord'].get('lat', 0.0)
86+
elif 'lat' in the_dict and 'lon' in the_dict:
87+
lat = the_dict['lat']
88+
lon = the_dict['lon']
8689
else:
8790
raise KeyError("Impossible to read geographical coordinates from JSON")
8891
if 'country' in data:

sphinx/index.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ With PyOWM you can interact programmatically with the following OpenWeatherMap w
3737
- **UV Index API v3.0**, offering data about Ultraviolet exposition
3838
- **Stations API v3.0**, allowing to create and manage meteostation and publish local weather measurements
3939
- **Weather Alerts API v3.0**, allowing to set triggers on weather conditions and areas and poll for spawned alerts
40+
- **Geocoding API v1.0** allowing to perform direct/reverse geocoding
4041

4142
And you can also get **image tiles** for several map layers provided by OWM
4243

@@ -183,6 +184,15 @@ Alerts API examples
183184

184185
v3/alerts-api-usage-examples
185186

187+
Geocoding API examples
188+
^^^^^^^^^^^^^^^^^^^^^^
189+
190+
.. toctree::
191+
:maxdepth: 1
192+
193+
v3/geocoding-api-usage-examples
194+
195+
186196
Map tiles client examples
187197
^^^^^^^^^^^^^^^^^^^^^^^^^
188198
.. toctree::

sphinx/pyowm.geocodingapi10.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pyowm.geocodingapi10 package
2+
============================
3+
4+
Submodules
5+
----------
6+
7+
pyowm.geocodingapi10.geocoding_manager module
8+
---------------------------------------------
9+
10+
.. automodule:: pyowm.geocodingapi10.geocoding_manager
11+
:members:
12+
:undoc-members:
13+
:show-inheritance:

sphinx/pyowm.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Subpackages
1010
pyowm.alertapi30
1111
pyowm.commons
1212
pyowm.airpollutionapi30
13+
pyowm.geocodingapi10
1314
pyowm.stationsapi30
1415
pyowm.tiles
1516
pyowm.utils

0 commit comments

Comments
 (0)