Skip to content

Commit

Permalink
Integrate IGNFrance
Browse files Browse the repository at this point in the history
  • Loading branch information
ijl committed Dec 30, 2014
1 parent 6a09f7f commit 5b0dfc4
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 148 deletions.
1 change: 0 additions & 1 deletion Makefile
Expand Up @@ -17,7 +17,6 @@ pylint:
pylint --rcfile .pylintrc geopy

test:
make lint
nosetests --verbose --with-cover --cover-erase --cover-package=geopy

clean:
Expand Down
5 changes: 3 additions & 2 deletions README.md
Expand Up @@ -11,8 +11,8 @@ geocoders and other data sources.
geopy includes geocoder classes for the [OpenStreetMap Nominatim][osm],
[ESRI ArcGIS][arcgis], [Google Geocoding API (V3)][googlev3],
[Baidu Maps][baidu], [Bing Maps API][bing], [Yahoo! PlaceFinder][placefinder],
[Yandex][yandex], [GeoNames][geonames], [MapQuest][mapquest],
[OpenMapQuest][openmapquest], [What3Words][what3words],
[Yandex][yandex], [IGNFrance][ignfrance], [GeoNames][geonames],
[MapQuest][mapquest], [OpenMapQuest][openmapquest], [What3Words][what3words],
[OpenCage][opencage], [SmartyStreets][smartystreets], [geocoder.us][dot_us],
and [GeocodeFarm][geocodefarm] geocoder services.
The various geocoder classes are located in [geopy.geocoders][geocoders_src].
Expand All @@ -24,6 +24,7 @@ The various geocoder classes are located in [geopy.geocoders][geocoders_src].
[geocodefarm]: https://www.geocodefarm.com/
[geonames]: http://www.geonames.org/
[googlev3]: https://developers.google.com/maps/documentation/geocoding/
[ignfrance]: http://api.ign.fr/tech-docs-js/fr/developpeur/search.html
[mapquest]: http://www.mapquestapi.com/geocoding/
[opencage]: http://geocoder.opencagedata.com/api.html
[openmapquest]: http://developer.mapquest.com/web/products/open/geocoding-service
Expand Down
3 changes: 3 additions & 0 deletions docs/index.rst
Expand Up @@ -33,6 +33,9 @@ Geocoders
.. autoclass:: geopy.geocoders.GoogleV3
:members: __init__, geocode, reverse, timezone

.. autoclass:: geopy.geocoders.IGNFrance
:members: __init__, geocode, reverse

.. autoclass:: geopy.geocoders.LiveAddress
:members: __init__, geocode

Expand Down
2 changes: 1 addition & 1 deletion geopy/geocoders/dot_us.py
Expand Up @@ -101,7 +101,7 @@ def geocode(self, query, exactly_one=True, timeout=None):
))
url = Request(url, headers={"Authorization": auth})
page = self._call_geocoder(url, timeout=timeout, raw=True)
content = page.read().decode("utf-8") if py3k else page.read() # pylint: disable=E1101
content = page.read().decode("utf-8") if py3k else page.read() # pylint: disable=E1101,E1103
places = [
r for r in csv.reader(
[content, ] if not isinstance(content, list)
Expand Down
128 changes: 59 additions & 69 deletions geopy/geocoders/ignfrance.py
Expand Up @@ -17,7 +17,8 @@

__all__ = ("IGNFrance", )

class IGNFrance(Geocoder):

class IGNFrance(Geocoder): # pylint: disable=W0223
"""
Geocoder using the IGN France GeoCoder OpenLS API. Documentation at:
http://api.ign.fr/tech-docs-js/fr/developpeur/search.html
Expand All @@ -28,7 +29,7 @@ class IGNFrance(Geocoder):
xmlns="http://www.opengis.net/xls"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/xls
xsi:schemaLocation="http://www.opengis.net/xls
http://schemas.opengis.net/ols/1.2/olsAll.xsd">
<RequestHeader srsName="epsg:4326"/>
<Request methodName="{method_name}"
Expand Down Expand Up @@ -90,18 +91,22 @@ def __init__(
scheme=scheme, timeout=timeout, proxies=proxies
)

# Catch if no api key with username and password
# Catch if no api key with username and password
# or no api key with referer
if not (api_key and password and username) \
if not (api_key and username and password) \
and not (api_key and referer):
raise ConfigurationError('You should provide an api key and a '
'username with a password or an api '
'key with a referer depending on '
'created api key')
if password and username and referer:
if (username and password) and referer:
raise ConfigurationError('You can\'t set username/password and '
'referer together. The API key always '
'differs depending on both scenarios')
if username and not password:
raise ConfigurationError(
'username and password must be set together'
)

self.api_key = api_key
self.username = username
Expand Down Expand Up @@ -202,8 +207,6 @@ def geocode(
filtering=filtering
)

logger.debug("Request geocode: \n %s", request_string)

params = {
'xls': request_string
}
Expand All @@ -212,7 +215,7 @@ def geocode(

logger.debug("%s.geocode: %s", self.__class__.__name__, url)

raw_xml = self.request_raw_content(url, timeout)
raw_xml = self._request_raw_content(url, timeout)

return self._parse_xml(
raw_xml,
Expand All @@ -223,9 +226,9 @@ def geocode(
def reverse(
self,
query,
reverse_geocode_preference=None,
reverse_geocode_preference=('StreetAddress', ),
maximum_responses=25,
filtering=None,
filtering='',
exactly_one=False,
timeout=None
): # pylint: disable=W0221,R0913
Expand Down Expand Up @@ -276,45 +279,32 @@ def reverse(
maximum_responses=maximum_responses
)

if reverse_geocode_preference is None:
reverse_geocode_preference = ['StreetAddress']

if not ('StreetAddress' in reverse_geocode_preference or
'PositionOfInterest' in reverse_geocode_preference):
raise GeocoderQueryError("""You must provide a list with
StreetAddress, PositionOfInterest or both for the request""")
for pref in reverse_geocode_preference:
if pref not in ('StreetAddress', 'PositionOfInterest'):
raise GeocoderQueryError(
'`reverse_geocode_preference` must contain '
'one or more of: StreetAddress, PositionOfInterest'
)

point = self._coerce_point_to_string(query)
point = point.replace(',', ' ')
point = self._coerce_point_to_string(query).replace(',', ' ')
reverse_geocode_preference = '\n'.join((
'<ReverseGeocodePreference>%s</ReverseGeocodePreference>' % pref
for pref
in reverse_geocode_preference
))

# Manage filtering value
if filtering is None:
filtering = ''

reverse_geocode_preference = [
('<ReverseGeocodePreference>' +
pref +
'</ReverseGeocodePreference>')
for pref in reverse_geocode_preference]
reverse_geocode_preference = '\n'.join(reverse_geocode_preference)
request_string = xml_request.format(
maximum_responses=maximum_responses,
query=point,
reverse_geocode_preference=reverse_geocode_preference,
filtering=filtering
)

logger.debug("Request reverse geocode: \n %s", request_string)

params = {
'xls': request_string
}

url = "?".join((self.api, urlencode(params)))
url = "?".join((self.api, urlencode({'xls': request_string})))

logger.debug("%s.reverse: %s", self.__class__.__name__, url)

raw_xml = self.request_raw_content(url, timeout)
raw_xml = self._request_raw_content(url, timeout)

return self._parse_xml(
raw_xml,
Expand Down Expand Up @@ -403,10 +393,10 @@ def remove_namespace(doc, namespace):
places = self._xml_to_json_places(tree, is_reverse=is_reverse)

if exactly_one:
return self.parse_place(places[0], is_freeform=is_freeform)
return self._parse_place(places[0], is_freeform=is_freeform)
else:
return [
self.parse_place(
self._parse_place(
place,
is_freeform=is_freeform
) for place in places
Expand All @@ -418,9 +408,11 @@ def _xml_to_json_places(tree, is_reverse=False):
Transform the xml ElementTree due to XML webservice return to json
"""

select_multi = 'GeocodedAddress'
if is_reverse:
select_multi = 'ReverseGeocodedLocation'
select_multi = (
'GeocodedAddress'
if not is_reverse
else 'ReverseGeocodedLocation'
)

adresses = tree.findall('.//' + select_multi)
places = []
Expand Down Expand Up @@ -496,7 +488,7 @@ def testContentAttrib(selector, key):

return places

def request_raw_content(self, url, timeout):
def _request_raw_content(self, url, timeout):
"""
Send the request to get raw content.
"""
Expand All @@ -512,13 +504,11 @@ def request_raw_content(self, url, timeout):
deserializer=None
)

logger.debug("Returned geocode: \n %s", raw_xml)

return raw_xml


@staticmethod
def parse_place(place, is_freeform=None):
def _parse_place(place, is_freeform=None):
"""
Get the location, lat, lng and place from a single json place.
"""
Expand All @@ -528,38 +518,38 @@ def parse_place(place, is_freeform=None):
else:
# When classic geocoding
if place.get('match_type'):
location = place.get('postal_code') + ' ' + \
place.get('commune')
location = "%s %s" % (
place.get('postal_code', ''),
place.get('commune', ''),
)
if place.get('match_type') in ['Street number',
'Street enhanced']:
location = place.get('building') + ' ' + \
place.get('street') + ', ' + \
location
location = "%s %s, %s" % (
place.get('building', ''),
place.get('street', ''),
location,
)
elif place.get('match_type') == 'Street':
location = place.get('street') + ', ' + \
location
location = "%s, %s" % (place.get('street', ''), location)
else:
# For parcelle
if place.get('numero'):
location = place.get('street')
else:
# When reverse geocoding
location = place.get('postal_code') + ' ' + \
place.get('commune')
location = "%s %s" % (
place.get('postal_code', ''),
place.get('commune', ''),
)
if place.get('street'):
location = place.get('street') + ', ' + \
location
location = "%s, %s" % (
place.get('street', ''),
location,
)
if place.get('building'):
location = place.get('building') + ' ' + \
location

latitude = place.get('lat')
longitude = place.get('lng')

return Location(location, (latitude, longitude), place)
location = "%s %s" % (
place.get('building', ''),
location,
)

def _parse_json(self, page, exactly_one=True):
"""
Returns location, (latitude, longitude) from json feed
"""
return page
return Location(location, (place.get('lat'), place.get('lng')), place)

0 comments on commit 5b0dfc4

Please sign in to comment.