diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2f72e92
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+geoapi.ini
+.idea/
diff --git a/distance-calc.iml b/distance-calc.iml
new file mode 100755
index 0000000..ad3c0a3
--- /dev/null
+++ b/distance-calc.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/geoapi.ini.template b/geoapi.ini.template
new file mode 100755
index 0000000..3211e18
--- /dev/null
+++ b/geoapi.ini.template
@@ -0,0 +1,12 @@
+[google]
+apikey=<>
+
+[mapquest]
+apikey=<>
+secret=<>
+
+[geocodio]
+apikey=<>
+
+[locationiq]
+token=<>
diff --git a/src/gcoder.py b/src/gcoder.py
new file mode 100755
index 0000000..c60413c
--- /dev/null
+++ b/src/gcoder.py
@@ -0,0 +1,119 @@
+# 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:]))