# GCoder.py: basic geocoding test import sys import argparse import json import configparser import urllib.parse import urllib.request class GeocodingError(RuntimeError): pass def geocode_google(config, address): apikey = config['google']['apikey'] if not apikey: raise GeocodingError("Google API key not specified") query = { 'key': apikey, 'address': address, 'region': 'us'} url = 'https://maps.googleapis.com/maps/api/geocode/json?' + \ urllib.parse.urlencode(query, quote_via=urllib.parse.quote) with urllib.request.urlopen(url) as response: if response.status == 200: apireturn = json.loads(response.read()) stat = apireturn['status'] if stat == 'OK': results = apireturn['results'] if len(results) > 1: raise GeocodingError(f"Google API returned ambiguous results (total count {len(results)})") coords = results[0]['geometry']['location'] return coords['lat'], coords['lng'] else: raise GeocodingError(f"Google API returns status of {stat}") else: raise GeocodingError(f"Google API returns {response.status} HTTP status code") def geocode_mapquest(config, address): apikey = config['mapquest']['apikey'] if not apikey: raise GeocodingError("MapQuest API key not specified") query = { 'key': apikey, 'inFormat': 'kvp', 'outFormat': 'json', 'location': address, 'thumbMaps': 'false', 'maxResults': 1} url = 'https://www.mapquestapi.com/geocoding/v1/address?' + urllib.parse.urlencode(query) with urllib.request.urlopen(url) as response: if response.status == 200: apireturn = json.loads(response.read()) stat = apireturn['info']['statuscode'] if stat == 0: results = apireturn['results'] if len(results) > 1: raise GeocodingError(f"MapQuest API returned ambiguous results (total count {len(results)})") locations = results[0]['locations'] if len(locations) > 1: raise GeocodingError(f"MapQuest API returned ambiguous locations (total count {len(locations)})") coords = locations[0]['latLng'] return coords['lat'], coords['lng'] else: raise GeocodingError(f"MapQuest API returns status of {stat}") else: raise GeocodingError(f"MapQuest API returns {response.status} HTTP status code") def geocode_geocodio(config, address): apikey = config['geocodio']['apikey'] if not apikey: raise GeocodingError("Geocodio API key not specified") query = {'q': address, 'api_key': apikey} url = 'https://api.geocod.io/v1.6/geocode?' + urllib.parse.urlencode(query) with urllib.request.urlopen(url) as response: if response.status == 200: apireturn = json.loads(response.read()) coords = apireturn['results'][0]['location'] return coords['lat'], coords['lng'] else: raise GeocodingError(f"Geocodio API returns {response.status} HTTP status code") def geocode_locationiq(config, address): apikey = config['locationiq']['token'] if not apikey: raise GeocodingError("LocationIQ API key not specified") query = {'q': address, 'key': apikey, 'format': 'json', 'limit': 1} url = 'https://us1.locationiq.com/v1/search.php?' + urllib.parse.urlencode(query, quote_via=urllib.parse.quote) with urllib.request.urlopen(url) as response: if response.status == 200: apireturn = json.loads(response.read()) place = apireturn[0] return place['lat'], place['lon'] else: raise GeocodingError(f"LocationIQ API returns {response.status} HTTP status code") geocoding_procs = { 'google': geocode_google, 'mapquest': geocode_mapquest, 'geocodio': geocode_geocodio, 'locationiq': geocode_locationiq } cmdline_parser = argparse.ArgumentParser() cmdline_parser.add_argument('address', nargs='+', help='The address to be geocoded') cmdline_parser.add_argument('-C', '--config', default='geoapi.ini', help='The geocoding API configuration file') cmdline_parser.add_argument('-g', '--geocoder', default='google', choices=geocoding_procs.keys(), help='Geocoding processor to use to get coordinates (default: google)') def main(args): opts = cmdline_parser.parse_args(args) config = configparser.ConfigParser() config.read(opts.config) my_address = ' '.join(opts.address) print(f"Address: '{my_address}'") coords = geocoding_procs[opts.geocoder](config, my_address) print(f"Coordinates: Latitude {coords[0]}, longitude {coords[1]}") if __name__ == '__main__': sys.exit(main(sys.argv[1:]))