Geocoding#

GeoPandas supports geocoding (i.e., converting place names to location on Earth) through geopy, an optional dependency of GeoPandas. The following example shows how to get the locations of boroughs in New York City, and plots those locations along with the detailed borough boundary file included within GeoPandas.

In [1]: import geodatasets

In [2]: boros = geopandas.read_file(geodatasets.get_path("nybb"))

In [3]: boros.BoroName
Out[3]: 
0    Staten Island
1           Queens
2         Brooklyn
3        Manhattan
4            Bronx
Name: BoroName, dtype: str

In [4]: boro_locations = geopandas.geocode(boros.BoroName)
---------------------------------------------------------------------------
TimeoutError                              Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:540, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
    539 try:
--> 540     response = conn.getresponse()
    541 except (BaseSSLError, OSError) as e:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connection.py:638, in HTTPConnection.getresponse(self)
    637 # Get the response from http.client.HTTPConnection
--> 638 httplib_response = super().getresponse()
    640 try:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/http/client.py:1478, in HTTPConnection.getresponse(self)
   1477 try:
-> 1478     response.begin()
   1479 except ConnectionError:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/http/client.py:343, in HTTPResponse.begin(self)
    342 for _ in range(_MAXINTERIMRESPONSES):
--> 343     version, status, reason = self._read_status()
    344     if status != CONTINUE:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/http/client.py:304, in HTTPResponse._read_status(self)
    303 def _read_status(self):
--> 304     line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
    305     if len(line) > _MAXLINE:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/socket.py:723, in SocketIO.readinto(self, b)
    722 try:
--> 723     return self._sock.recv_into(b)
    724 except timeout:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/ssl.py:1304, in SSLSocket.recv_into(self, buffer, nbytes, flags)
   1301         raise ValueError(
   1302           "non-zero flags not allowed in calls to recv_into() on %s" %
   1303           self.__class__)
-> 1304     return self.read(nbytes, buffer)
   1305 else:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/ssl.py:1138, in SSLSocket.read(self, len, buffer)
   1137 if buffer is not None:
-> 1138     return self._sslobj.read(len, buffer)
   1139 else:

TimeoutError: The read operation timed out

The above exception was the direct cause of the following exception:

ReadTimeoutError                          Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:793, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    792 # Make the request on the HTTPConnection object
--> 793 response = self._make_request(
    794     conn,
    795     method,
    796     url,
    797     timeout=timeout_obj,
    798     body=body,
    799     headers=headers,
    800     chunked=chunked,
    801     retries=retries,
    802     response_conn=response_conn,
    803     preload_content=preload_content,
    804     decode_content=decode_content,
    805     **response_kw,
    806 )
    808 # Everything went great!

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:542, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
    541 except (BaseSSLError, OSError) as e:
--> 542     self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
    543     raise

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:373, in HTTPConnectionPool._raise_timeout(self, err, url, timeout_value)
    372 if isinstance(err, SocketTimeout):
--> 373     raise ReadTimeoutError(
    374         self, url, f"Read timed out. (read timeout={timeout_value})"
    375     ) from err
    377 # See the above comment about EAGAIN in Python 3.

ReadTimeoutError: HTTPSConnectionPool(host='photon.komoot.io', port=443): Read timed out. (read timeout=1)

The above exception was the direct cause of the following exception:

MaxRetryError                             Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/requests/adapters.py:696, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    695 try:
--> 696     resp = conn.urlopen(
    697         method=request.method,
    698         url=url,
    699         body=request.body,  # type: ignore[arg-type]  # urllib3 stubs don't accept Iterable[bytes | str]
    700         headers=request.headers,  # type: ignore[arg-type]  # urllib3#3072
    701         redirect=False,
    702         assert_same_host=False,
    703         preload_content=False,
    704         decode_content=False,
    705         retries=self.max_retries,
    706         timeout=resolved_timeout,
    707         chunked=chunked,
    708     )
    710 except (ProtocolError, OSError) as err:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:886, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    874     log.warning(
    875         "Retrying (%r) after connection broken by '%r': %s",
    876         retries,
   (...)    884         extra={"__urllib3-retry-warning": {"host": self.host}},
    885     )
--> 886     return self.urlopen(
    887         method,
    888         url,
    889         body,
    890         headers,
    891         retries,
    892         redirect,
    893         assert_same_host,
    894         timeout=timeout,
    895         pool_timeout=pool_timeout,
    896         release_conn=release_conn,
    897         chunked=chunked,
    898         body_pos=body_pos,
    899         preload_content=preload_content,
    900         decode_content=decode_content,
    901         **response_kw,
    902     )
    904 # Handle redirect?

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:886, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    874     log.warning(
    875         "Retrying (%r) after connection broken by '%r': %s",
    876         retries,
   (...)    884         extra={"__urllib3-retry-warning": {"host": self.host}},
    885     )
--> 886     return self.urlopen(
    887         method,
    888         url,
    889         body,
    890         headers,
    891         retries,
    892         redirect,
    893         assert_same_host,
    894         timeout=timeout,
    895         pool_timeout=pool_timeout,
    896         release_conn=release_conn,
    897         chunked=chunked,
    898         body_pos=body_pos,
    899         preload_content=preload_content,
    900         decode_content=decode_content,
    901         **response_kw,
    902     )
    904 # Handle redirect?

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/connectionpool.py:847, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    845     new_e = ProtocolError("Connection aborted.", new_e)
--> 847 retries = retries.increment(
    848     method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2]
    849 )
    850 retries.sleep()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/urllib3/util/retry.py:555, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
    554     reason = error or ResponseError(cause)
--> 555     raise MaxRetryError(_pool, url, reason) from reason  # type: ignore[arg-type]
    557 log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)

MaxRetryError: HTTPSConnectionPool(host='photon.komoot.io', port=443): Max retries exceeded with url: /api?q=Staten+Island&limit=1 (Caused by ReadTimeoutError("HTTPSConnectionPool(host='photon.komoot.io', port=443): Read timed out. (read timeout=1)"))

During handling of the above exception, another exception occurred:

ConnectionError                           Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/geopy/adapters.py:482, in RequestsAdapter._request(self, url, timeout, headers)
    481 try:
--> 482     resp = self.session.get(url, timeout=timeout, headers=headers)
    483 except Exception as error:

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/requests/sessions.py:671, in Session.get(self, url, params, **kwargs)
    670 kwargs.setdefault("allow_redirects", True)
--> 671 return self.request("GET", url, params=params, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/requests/sessions.py:651, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    650 send_kwargs.update(settings)
--> 651 resp = self.send(prep, **send_kwargs)
    653 return resp

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/requests/sessions.py:784, in Session.send(self, request, **kwargs)
    783 # Send the request
--> 784 r = adapter.send(request, **kwargs)
    786 # Total elapsed time of the request (approximately)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/requests/adapters.py:729, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    727         raise SSLError(e, request=request)
--> 729     raise ConnectionError(e, request=request)
    731 except ClosedPoolError as e:

ConnectionError: HTTPSConnectionPool(host='photon.komoot.io', port=443): Max retries exceeded with url: /api?q=Staten+Island&limit=1 (Caused by ReadTimeoutError("HTTPSConnectionPool(host='photon.komoot.io', port=443): Read timed out. (read timeout=1)"))

During handling of the above exception, another exception occurred:

GeocoderUnavailable                       Traceback (most recent call last)
Cell In[4], line 1
----> 1 boro_locations = geopandas.geocode(boros.BoroName)

File ~/checkouts/readthedocs.org/user_builds/geopandas/checkouts/stable/geopandas/tools/geocoding.py:66, in geocode(strings, provider, **kwargs)
     63     provider = "photon"
     64 throttle_time = _get_throttle_time(provider)
---> 66 return _query(strings, True, provider, throttle_time, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/geopandas/checkouts/stable/geopandas/tools/geocoding.py:139, in _query(data, forward, provider, throttle_time, **kwargs)
    137 try:
    138     if forward:
--> 139         results[i] = coder.geocode(s)
    140     else:
    141         results[i] = coder.reverse((s.y, s.x), exactly_one=True)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/geopy/geocoders/photon.py:166, in Photon.geocode(self, query, exactly_one, timeout, location_bias, language, limit, osm_tag, bbox)
    164 logger.debug("%s.geocode: %s", self.__class__.__name__, url)
    165 callback = partial(self._parse_json, exactly_one=exactly_one)
--> 166 return self._call_geocoder(url, callback, timeout=timeout)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/geopy/geocoders/base.py:367, in Geocoder._call_geocoder(self, url, callback, timeout, is_json, headers)
    365 try:
    366     if is_json:
--> 367         result = self.adapter.get_json(url, timeout=timeout, headers=req_headers)
    368     else:
    369         result = self.adapter.get_text(url, timeout=timeout, headers=req_headers)

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/geopy/adapters.py:472, in RequestsAdapter.get_json(self, url, timeout, headers)
    471 def get_json(self, url, *, timeout, headers):
--> 472     resp = self._request(url, timeout=timeout, headers=headers)
    473     try:
    474         return resp.json()

File ~/checkouts/readthedocs.org/user_builds/geopandas/conda/stable/lib/python3.13/site-packages/geopy/adapters.py:494, in RequestsAdapter._request(self, url, timeout, headers)
    492         raise GeocoderServiceError(message)
    493     else:
--> 494         raise GeocoderUnavailable(message)
    495 elif isinstance(error, requests.Timeout):
    496     raise GeocoderTimedOut("Service timed out")

GeocoderUnavailable: HTTPSConnectionPool(host='photon.komoot.io', port=443): Max retries exceeded with url: /api?q=Staten+Island&limit=1 (Caused by ReadTimeoutError("HTTPSConnectionPool(host='photon.komoot.io', port=443): Read timed out. (read timeout=1)"))

In [5]: boro_locations
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 boro_locations

NameError: name 'boro_locations' is not defined

In [6]: import matplotlib.pyplot as plt

In [7]: fig, ax = plt.subplots()

In [8]: boros.to_crs("EPSG:4326").plot(ax=ax, color="white", edgecolor="black");

In [9]: boro_locations.plot(ax=ax, color="red");
../../_images/boro_centers_over_bounds.png

By default, the geocode() function uses the Photon geocoding API. But a different geocoding service can be specified with the provider keyword.

The argument to provider can either be a string referencing geocoding services, such as 'google', 'bing', 'yahoo', and 'openmapquest', or an instance of a Geocoder from geopy. See geopy.geocoders.SERVICE_TO_GEOCODER for the full list. For many providers, parameters such as API keys need to be passed as **kwargs in the geocode() call.

For example, to use the OpenStreetMap Nominatim geocoder, you need to specify a user agent:

geopandas.geocode(boros.BoroName, provider='nominatim', user_agent="my-application")

Attention

Please consult the Terms of Service for the chosen provider. The example above uses 'photon' (the default), which expects fair usage - extensive usage will be throttled. (Photon’s Terms of Use).