1

I have this dataframe:

      Lat       Lon
 29.39291 -98.50925
 29.39923 -98.51256
 29.40147 -98.51123
 29.38752 -98.52372
 29.39291 -98.50925
 29.39537 -98.50402
 29.39343 -98.49707
 29.39291 -98.50925
 29.39556 -98.53148

I want to convert these lat/lon rows to ZIP codes (for each row), using Python.

1
  • Not sure what your exact error/problem is, but pygeocoder hasn't seen an update in a while, maybe checkout geocoder. Note, that you need to set env variables with an API key you've created from a Google Account. I haven't tried the Bing provider, but that is also an option with geocoder.
    – Ryan
    Mar 4, 2020 at 19:35

3 Answers 3

9

You can use geopy and its Nominatim geocoder.

Here is an example using the DataFrame you provided:

import geopy
import pandas as pd


def get_zipcode(df, geolocator, lat_field, lon_field):
    location = geolocator.reverse((df[lat_field], df[lon_field]))
    return location.raw['address']['postcode']


geolocator = geopy.Nominatim(user_agent='my-application')

df = pd.DataFrame({
    'Lat': [29.39291, 29.39923, 29.40147, 29.38752, 29.39291, 29.39537, 29.39343, 29.39291, 29.39556],
    'Lon': [-98.50925, -98.51256, -98.51123, -98.52372, -98.50925, -98.50402, -98.49707, -98.50925, -98.53148]
})
zipcodes = df.apply(get_zipcode, axis=1, geolocator=geolocator, lat_field='Lat', lon_field='Lon')
>>> zipcodes
0    78204
1    78204
2    78204
3    78225
4    78204
5    78204
6    78204
7    78204
8    78225
dtype: object
2

For geocoding with ArcGIS, credentials have to be provided gis = GIS("http://www.arcgis.com", "username", "password").


In terms of pandas, this code should do the work (adapted from @Marcelo Villa's answer)

from arcgis.geocoding import reverse_geocode
from arcgis.geometry import Geometry
from arcgis.gis import GIS
import pandas as pd

gis = GIS("http://www.arcgis.com", "***", "***")

def get_zip(df, lon_field, lat_field):
    location = reverse_geocode((Geometry({"x":float(df[lon_field]), "y":float(df[lat_field]), "spatialReference":{"wkid": 4326}})))
    return location['address']['Postal']

df = pd.DataFrame({
    'Lat': [29.39291, 29.39923, 29.40147, 29.38752, 29.39291, 29.39537, 29.39343, 29.39291, 29.39556],
    'Lon': [-98.50925, -98.51256, -98.51123, -98.52372, -98.50925, -98.50402, -98.49707, -98.50925, -98.53148]
})

zipcodes = df.apply(get_zip, axis=1, lat_field='Lat', lon_field='Lon')

result


Probably not as smart as pandas but using Python's internal module csv and ArcGIS's API for Python Reverse Geocoding.

from arcgis.geocoding import reverse_geocode
from arcgis.geometry import Geometry
from arcgis.gis import GIS
import csv

gis = GIS("http://www.arcgis.com", "***", "***")

coords = [
(29.39291, -98.50925),
(29.39923, -98.51256),
(29.40147, -98.51123),
(29.38752, -98.52372),
(29.39291, -98.50925),
(29.39537, -98.50402),
(29.39343, -98.49707),
(29.39291, -98.50925),
(29.39556, -98.53148)
]

result = []

for lat, lon in coords:
    pt = Geometry({
        "x": float(lon),
        "y": float(lat),
        "spatialReference": {
            "wkid": 4326
        }
    })
    try:
        result.append({'lat': lat, 'lon': lon, 'geocoded': reverse_geocode(pt)})
    except:
        pass

result_zip = []

for item in result:
    result_item = {
        'lat': item['lat'],
        'lon': item['lon'],
        'zip': item['geocoded']['address']['Postal']
    }
    result_zip.append(result_item)

keys = result_zip[0].keys()

with open('output.csv', 'w', encoding='utf8', newline='') as output_file:
    dict_writer = csv.DictWriter(output_file, keys, delimiter=';', quoting=csv.QUOTE_NONE, lineterminator='\r')
    dict_writer.writeheader()
    dict_writer.writerows(result_zip)

Output csv-file looks as following

output

0

Simplifying Marcelo's answer, using only python and geopy (No Pandas):

import geopy
geo_locator = geopy.Nominatim(user_agent='1234')
                        # Latitude, Longitude
r = geo_locator.reverse((43.509004, -79.628853))
print(r.raw['address']['postcode'])
# L5J 2Y4
print(r.raw)
# {'place_id': 110711915, 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright', 'osm_type': 'way', 'osm_id': 33302111, 'lat': '43.50878025', 'lon': '-79.62891074069033', 'display_name': 'Canadian Tire, 900, Southdown Road, Clarkson, Mississauga, Peel Region, Golden Horseshoe, Ontario, L5J 2Y4, Canada', 'address': {'shop': 'Canadian Tire', 'house_number': '900', 'road': 'Southdown Road', 'suburb': 'Clarkson', 'city': 'Mississauga', 'county': 'Peel Region', 'state_district': 'Golden Horseshoe', 'state': 'Ontario', 'postcode': 'L5J 2Y4', 'country': 'Canada', 'country_code': 'ca'}, 'boundingbox': ['43.5083103', '43.5093261', '-79.6295956', '-79.6282259']}
# 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.