Skip to content

Instantly share code, notes, and snippets.

@psychemedia
Created May 7, 2021 10:37
Show Gist options
  • Save psychemedia/de0b88750dfac7fcba0dc4bb1158c045 to your computer and use it in GitHub Desktop.
Save psychemedia/de0b88750dfac7fcba0dc4bb1158c045 to your computer and use it in GitHub Desktop.
tm112-demo-rendered 1
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Activity 6.22 - 6.25 Geocoding\n",
"\n",
"In this notebook, you will learn how to geocode different sorts of location data by making requests to several online APIs (*Application Programming Interface*) for latitude and longitude co-ordinates associated with those locations.\n",
"\n",
"The aim of the notebook is *not* to teach you formal approaches for working with APIs or the data that is returned from them. Instead, it's something to whet your curiosity. Something to show you how, with a few lines of Python code, you can start to work with live, third-party datasources and online services to perform real-world programming tasks.\n",
"\n",
"If something doesn't work: *DON'T PANIC*. You won't break your computer and you won't break the internet. And you won't fail the module if you just move on!\n",
"\n",
"The location data we will consider includes:\n",
"\n",
"- <a href=\"#addresses\">Activity 6.22 - Geocoding an address using an API</a>\n",
"- <a href=\"#postcodes\">Activity 6.23 - Geocoding addresses and postcodes</a>\n",
"- <a href=\"#IPaddresses\">Activity 6.24 - Geocoding an IP address</a>\n",
"- <a href=\"#revgeopostcode\">Activity 6.25 - Reverse geocoding a postcode</a>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"addresses\"></a>\n",
"## Activity 6.22 - Geocoding an address using an API\n",
"\n",
"When Google Maps first appeared in 2005, it existed primarily as a browser based application. Developers soon started to identify the remote Google services that the application relied on and an official API soon followed, along with APIs for other geo-related services such as geocoding.\n",
"\n",
"Since then, a wide range of other providers have opened up publicly accessible geo-related services, including openly licensed mapping services such as OpenStreetmap, as well as geocoding and geolcation services.\n",
"\n",
"Many of these services are accessed programmatically using an API. Some of them, such as the Google APIs, require personal API keys which are associated with registered users and can be used to track, and bill for, usage. Other services, such as Open Street Map, and its associated [Nominatim](https://nominatim.org/release-docs/develop/api/Search/) service, still publish open endpoints that *do not* require an API key.\n",
"\n",
"Do not worry in this activity about the detail of how the call to the API is constructed using Python code or how the response is constructed. The main aim of this activity is simply to demonstrate that small amounts of code *can* be used to generate requests to third party online services, parse the response, and do something simple with it. However, if you do peek into the entrails of the data object that is returned, you may be able to identify recognisable things within it."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"#The requests library makes it easy to call URLs using Python\n",
"import requests"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Specify the address for which we want to find the geocoded location (you could use your own address here):"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"address='Open University, Walton Hall, Milton Keynes, MK7 6AA, UK'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can construct a URL to call on the Nomimatim web service using this address as an argument.\n",
"\n",
"Note that we are also requesting that the service provides a response using JSON (Javascript Object Notation) formatted data."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"[]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Construct a request to the Nominatim geolocatio service using our desired address\n",
"r= requests.get(\"https://nominatim.openstreetmap.org/search\", params={'q': address, 'format':'json'})\n",
"r.json()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Try rerunning the previous cell with an address that is familiar to you. Does the API find it?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Optional Activities\n",
"\n",
"- try to display the location of an address you have geolocated on a map using `folium_magic`. Remember, you can pass in the co-ordinates literally; for example: `%folium_map -m 48.8584,2.2945 -z 17`"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"%load_ext folium_magic"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"#Go for it... :-)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"postcodes\"></a>\n",
"## Activity 6.23 - Geocoding addresses and postcodes\n",
"\n",
"Postcodes are a widely used form of location data, typically capable of identifying a location to a resolution of a few hundred square metres.\n",
"\n",
"There are several online services that will return geolocation information given a postcode. You have alreay seen how the Google geolocation API can geocode an address, and in many cases it can also geocode a postcode if it can identify it as such; providing a postcode + country name as the address can make this more reliable.\n",
"\n",
"As you are perhaps starting to see from these notebook activities, data is often returned from webservices using the JSON (Javascript Object Notation) data format, although some APIs allow you to specify other formats such as XML.\n",
"\n",
"(One advantage of the JSON response is that it can be immediately consumed by a Javascript script called from inside a webpage.)\n",
"\n",
"JSON and XML both allow data to be represented in a structured, *tree* based hierarchical format. In this notebook, as well exploring a new API published via the *postcodes.io* website, we'll start to look in a little bit more detail at how the JSON response is structured."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Looking at the data object returned from the `postcodes.io` API\n",
"\n",
"The *postcodes.io* API returns data as hierarchical, tree based JSON object. The tree has the following form:\n",
"\n",
"![Hierarchical structure of postocdes api data, showire results tree with latitude, longitude and codes children, and codes showing admin_district and parish district](images/postodes_io_struct.png)\n",
"\n",
"<!--- http://blockdiag.com/en/blockdiag/demo.html\n",
"{\n",
"\n",
" \"result\" -> \"postcode\";\n",
"\"result\" -> \"country\";\n",
"\"result\" -> \"longitude\";\n",
"\"result\" -> \"latitude\";\n",
"\"dots\" [shape = \"dots\"];\n",
"\"result\" -> \"dots\" [style = \"none\"];\n",
"\"result\" -> \"codes\";\n",
"\"codes\" -> \"admin_district\";\n",
"\"dots2\" [shape = \"dots\"];\n",
"\"codes\" -> \"dots2\" [style = \"none\"];\n",
"\"codes\" -> \"parish\";\n",
"\n",
"})\n",
"\n",
"--->\n",
"\n",
"The `result` node is at the top of the tree with children `postcode`, `latitude`, `longitude` and so on. The `codes` child has further children, such as: `admin` and `parish`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In *python*, data structures of this form can be represented using the `dict` (\"dictionary\") structure, which you will meet elsewhere in the course.\n",
"\n",
"The *python* `requests` library has a method that parses a correctly formed JSON response as a *python* `dict`, or more generally, as a set of *nested dicts*. In this case, one `dict` structure may be nested inside another to support child, grandchild, great grandchild, and so on, levels of structure.\n",
"\n",
"![Hierarchical structure of postocdes api data, showire results tree with latitude, longitude and codes children, and codes showing admin_district and parish district](images/postodes_io_struct_data.png)\n",
"\n",
"The contents of different levels of the nested `dict` data structure can be accessed by using a form of associative, relative addressing. For example, if the variable `mypostcode` is set to the `dict` shown above, we could access the contents of the main `result` part of the data structure by writing: `mypostcode[\"result\"]`.\n",
"\n",
"To obtain the value of items in deeper nested parts of the data structure, we simply add further levels of relative addressing. To fetch the value of the `postcode`, we need to specify the path to it via the `result` node: `mypostcode[\"result\"][\"postcode\"]`. To obtain the value of the `parish` in the `code` part of the data structure, we specify the path to it as `mypostcode[\"result\"][\"code\"][\"parish\"]`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run the following cells to call the `postcodes.io` API with a particular postcode.\n",
"\n",
"See if you can make sense of the result that is returned."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"#The requests library makes it easy to call URLs using Python\n",
"import requests"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'status': 200,\n",
" 'result': {'postcode': 'MK7 6AA',\n",
" 'quality': 1,\n",
" 'eastings': 488625,\n",
" 'northings': 237063,\n",
" 'country': 'England',\n",
" 'nhs_ha': 'South Central',\n",
" 'longitude': -0.709747,\n",
" 'latitude': 52.024914,\n",
" 'european_electoral_region': 'South East',\n",
" 'primary_care_trust': 'Milton Keynes',\n",
" 'region': 'South East',\n",
" 'lsoa': 'Milton Keynes 017C',\n",
" 'msoa': 'Milton Keynes 017',\n",
" 'incode': '6AA',\n",
" 'outcode': 'MK7',\n",
" 'parliamentary_constituency': 'Milton Keynes South',\n",
" 'admin_district': 'Milton Keynes',\n",
" 'parish': 'Walton',\n",
" 'admin_county': None,\n",
" 'admin_ward': 'Monkston',\n",
" 'ced': None,\n",
" 'ccg': 'NHS Milton Keynes',\n",
" 'nuts': 'Milton Keynes',\n",
" 'codes': {'admin_district': 'E06000042',\n",
" 'admin_county': 'E99999999',\n",
" 'admin_ward': 'E05009415',\n",
" 'parish': 'E04001275',\n",
" 'parliamentary_constituency': 'E14000822',\n",
" 'ccg': 'E38000107',\n",
" 'ccg_id': '04F',\n",
" 'ced': 'E99999999',\n",
" 'nuts': 'UKJ12',\n",
" 'lsoa': 'E01016820',\n",
" 'msoa': 'E02003475',\n",
" 'lau2': 'E05009415'}}}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"postcode = 'MK7 6AA'\n",
"r=requests.get('https://api.postcodes.io/postcodes/{PC}'.format(PC=postcode))\n",
"r.json()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Try rerunning the previous cell using different postcodes - can the service locate your home postcode?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Parsing the `postcodes.io` JSON data\n",
"\n",
"Once we have retrieved the data from the API, and cast it as a *python* data object, we can look inside it programmatically.\n",
"\n",
"We can also create variables that contain subsets of the response data."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'status': 200,\n",
" 'result': {'postcode': 'MK7 6AA',\n",
" 'quality': 1,\n",
" 'eastings': 488625,\n",
" 'northings': 237063,\n",
" 'country': 'England',\n",
" 'nhs_ha': 'South Central',\n",
" 'longitude': -0.709747,\n",
" 'latitude': 52.024914,\n",
" 'european_electoral_region': 'South East',\n",
" 'primary_care_trust': 'Milton Keynes',\n",
" 'region': 'South East',\n",
" 'lsoa': 'Milton Keynes 017C',\n",
" 'msoa': 'Milton Keynes 017',\n",
" 'incode': '6AA',\n",
" 'outcode': 'MK7',\n",
" 'parliamentary_constituency': 'Milton Keynes South',\n",
" 'admin_district': 'Milton Keynes',\n",
" 'parish': 'Walton',\n",
" 'admin_county': None,\n",
" 'admin_ward': 'Monkston',\n",
" 'ced': None,\n",
" 'ccg': 'NHS Milton Keynes',\n",
" 'nuts': 'Milton Keynes',\n",
" 'codes': {'admin_district': 'E06000042',\n",
" 'admin_county': 'E99999999',\n",
" 'admin_ward': 'E05009415',\n",
" 'parish': 'E04001275',\n",
" 'parliamentary_constituency': 'E14000822',\n",
" 'ccg': 'E38000107',\n",
" 'ccg_id': '04F',\n",
" 'ced': 'E99999999',\n",
" 'nuts': 'UKJ12',\n",
" 'lsoa': 'E01016820',\n",
" 'msoa': 'E02003475',\n",
" 'lau2': 'E05009415'}}}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"json = r.json()\n",
"json"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'postcode': 'MK7 6AA',\n",
" 'quality': 1,\n",
" 'eastings': 488625,\n",
" 'northings': 237063,\n",
" 'country': 'England',\n",
" 'nhs_ha': 'South Central',\n",
" 'longitude': -0.709747,\n",
" 'latitude': 52.024914,\n",
" 'european_electoral_region': 'South East',\n",
" 'primary_care_trust': 'Milton Keynes',\n",
" 'region': 'South East',\n",
" 'lsoa': 'Milton Keynes 017C',\n",
" 'msoa': 'Milton Keynes 017',\n",
" 'incode': '6AA',\n",
" 'outcode': 'MK7',\n",
" 'parliamentary_constituency': 'Milton Keynes South',\n",
" 'admin_district': 'Milton Keynes',\n",
" 'parish': 'Walton',\n",
" 'admin_county': None,\n",
" 'admin_ward': 'Monkston',\n",
" 'ced': None,\n",
" 'ccg': 'NHS Milton Keynes',\n",
" 'nuts': 'Milton Keynes',\n",
" 'codes': {'admin_district': 'E06000042',\n",
" 'admin_county': 'E99999999',\n",
" 'admin_ward': 'E05009415',\n",
" 'parish': 'E04001275',\n",
" 'parliamentary_constituency': 'E14000822',\n",
" 'ccg': 'E38000107',\n",
" 'ccg_id': '04F',\n",
" 'ced': 'E99999999',\n",
" 'nuts': 'UKJ12',\n",
" 'lsoa': 'E01016820',\n",
" 'msoa': 'E02003475',\n",
" 'lau2': 'E05009415'}}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results = json['result']\n",
"results"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's extract the latitude and longitude values. We can reach them in a variety of ways."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"52.024914 -0.709747\n"
]
}
],
"source": [
"#Obtain the lat/long of a postcode\n",
"lat=r.json()['result']['latitude']\n",
"\n",
"lon = results['longitude']\n",
"\n",
"#Display the result\n",
"print(lat,lon)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Optional Activities\n",
"\n",
"- use `folium magic` to map the location of an address you have provided;\n",
"- see if you can create a variable that contains the formatted address of a location object retrieved from the postcods.io API;\n",
"- see if you can write a loop that will look up the geolocations of several postcodes, one at a time, appending each response object to a list of responses. To be nice to the API import the *python* `time` library and add the statement `time.sleep(1)` inside the loop to pause its execution for one second during each iteration. \n",
"- use some `folium_magic` to display one or markers for each of your (looped) postcodes. You can use the `-M` switch to add multiple markers from a Python variable; pass in the name of a variable that refers to *without* the $ prefix. For example, if the markers are assigned to the variable `markers` use the magic `%folium_map -M markers`:\n",
" - a single `dict`, such as `markers={'lat':52.0250, 'lng':-0.7084,'popup':'Open University, Walton Hall'}`\n",
" - a single ordered `list`, such as `markers=[52.0250, -0.7084,'Open University, Walton Hall']`\n",
" - a `list` of `dict`s, such as `markers=[{'lat':52.0250, 'lng':-0.7084,'popup':'Open University, Walton Hall'},{'lat':52.0, 'lng':-0.70,'popup':'Open University, Walton Hall'}]`\n",
" - a `list` of ordered `lists`, such as `markers=[[52.0250, -0.7084,'Open University, Walton Hall'], [52., -0.7,'Open University, Walton Hall']]`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"IPaddresses\"></a>\n",
"## Activity 6.24 - Geocoding an IP address\n",
"\n",
"As well as looking up geolocation data for a *postal* address, we can also try to look up a location based on the IP address of a computer. There are seveal websites that allow you to lookup the IP address of the device you are using to connect to the internet, and several webservices too.\n",
"\n",
"One such service is the Amazon Web Servies (AWS) CheckIP service: http://checkip.amazonaws.com/ .\n",
"\n",
"We *could* look up the IP address of the computer the notebooks are running on, but that would return the IP address of the *server* the notebooks are running on:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The AWS `checkip` service returns an IP address terminated by an end of line (`\\n`) character. By using the `requests` library, we can call the URL, access the data response (`text`) and then strip (`.strip()`)) the end-of-line whitespace character from it. But if we're running the notebook on a cloud host, the IP address returned may not be very useful..."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"#The requests library makes it easy to call URLs using Python\n",
"import requests"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"'34.71.221.157'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"notebookIPaddress=requests.get('http://checkip.amazonaws.com/').text.strip()\n",
"notebookIPaddress"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Instead, vist the URL http://checkip.amazonaws.com in your brower and make a copy of the IP address it returns. Assign that address to the `myIPaddress` variable below:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"myIPaddress = 'your.ip.address.here'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As before, we need to construct a URL to call a appropriate IP geolocation service. The service I'm going to use is [ipstack.com](https://ipstack.com/quickstart). "
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"'http://api.ipstack.com/your.ip.address.here?access_key=d19510a436ddca4cb01822325ca40178'"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#We can construct a URL based around the IP address of the machine making the request as follows:\n",
"KEY='d19510a436ddca4cb01822325ca40178'\n",
"url='http://api.ipstack.com/{IP}?access_key={KEY}'.format(IP=myIPaddress,\n",
" KEY=KEY)\n",
"url"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As with the other APIs we have used, the response is provided as a JSON object. You may be getting a feel now for how we might be able to make the request and handle this response."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'ip': 'your.ip.address.here',\n",
" 'type': None,\n",
" 'continent_code': None,\n",
" 'continent_name': None,\n",
" 'country_code': None,\n",
" 'country_name': None,\n",
" 'region_code': None,\n",
" 'region_name': None,\n",
" 'city': None,\n",
" 'zip': None,\n",
" 'latitude': None,\n",
" 'longitude': None,\n",
" 'location': {'geoname_id': None,\n",
" 'capital': None,\n",
" 'languages': None,\n",
" 'country_flag': None,\n",
" 'country_flag_emoji': None,\n",
" 'country_flag_emoji_unicode': None,\n",
" 'calling_code': None,\n",
" 'is_eu': None}}"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"r=requests.get(url)\n",
"r.json()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you are using a commercial ISP when you look up your IP address via your browser location bar using https://freegeoip.net or http://checkip.amazonaws.com/ - for example, if you are using home broadband through a large ISP - you may find that the location identified is nowhere near you. In this case, the locatin may refer to the physical network datacentre that is managing your network connection.\n",
"\n",
"If you tried to geocode the IP address in `notebookIPaddress`, the result may surprise you. If the notebook and the python process associated with it is running on a server hosted in the cloud. In some cases, the response may often be empty as the location of the IP address is unknown - the physical location of the IP address may be hard to pin down if the servers they are assigned to are floating in the cloud!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"revgeopostcode\"></a>\n",
"## Activity 6.25 - Reverse geocoding a postcode\n",
"\n",
"*Reverse geocoding* refers to the ability to go *from* a geocoded location, represented as a latitude/longitude pair, for example, back to an address or location.\n",
"\n",
"The *postcodes.io* service supports reverse geocoding by accpeting a latitude/longitude pair and returning the postcode area associated with it, along with a data about other admonistrative gepgraphy regions that the location falls within."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"#Use the OU Walton Hall Location\n",
"lat, lon = (52.0250, -0.7084)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'status': 200,\n",
" 'result': [{'postcode': 'MK7 6AA',\n",
" 'quality': 1,\n",
" 'eastings': 488625,\n",
" 'northings': 237063,\n",
" 'country': 'England',\n",
" 'nhs_ha': 'South Central',\n",
" 'longitude': -0.709747,\n",
" 'latitude': 52.024914,\n",
" 'european_electoral_region': 'South East',\n",
" 'primary_care_trust': 'Milton Keynes',\n",
" 'region': 'South East',\n",
" 'lsoa': 'Milton Keynes 017C',\n",
" 'msoa': 'Milton Keynes 017',\n",
" 'incode': '6AA',\n",
" 'outcode': 'MK7',\n",
" 'parliamentary_constituency': 'Milton Keynes South',\n",
" 'admin_district': 'Milton Keynes',\n",
" 'parish': 'Walton',\n",
" 'admin_county': None,\n",
" 'admin_ward': 'Monkston',\n",
" 'ced': None,\n",
" 'ccg': 'NHS Milton Keynes',\n",
" 'nuts': 'Milton Keynes',\n",
" 'codes': {'admin_district': 'E06000042',\n",
" 'admin_county': 'E99999999',\n",
" 'admin_ward': 'E05009415',\n",
" 'parish': 'E04001275',\n",
" 'parliamentary_constituency': 'E14000822',\n",
" 'ccg': 'E38000107',\n",
" 'ccg_id': '04F',\n",
" 'ced': 'E99999999',\n",
" 'nuts': 'UKJ12',\n",
" 'lsoa': 'E01016820',\n",
" 'msoa': 'E02003475',\n",
" 'lau2': 'E05009415'},\n",
" 'distance': 92.95169474},\n",
" {'postcode': 'MK7 6AE',\n",
" 'quality': 1,\n",
" 'eastings': 488625,\n",
" 'northings': 237063,\n",
" 'country': 'England',\n",
" 'nhs_ha': 'South Central',\n",
" 'longitude': -0.709747,\n",
" 'latitude': 52.024914,\n",
" 'european_electoral_region': 'South East',\n",
" 'primary_care_trust': 'Milton Keynes',\n",
" 'region': 'South East',\n",
" 'lsoa': 'Milton Keynes 017C',\n",
" 'msoa': 'Milton Keynes 017',\n",
" 'incode': '6AE',\n",
" 'outcode': 'MK7',\n",
" 'parliamentary_constituency': 'Milton Keynes South',\n",
" 'admin_district': 'Milton Keynes',\n",
" 'parish': 'Walton',\n",
" 'admin_county': None,\n",
" 'admin_ward': 'Monkston',\n",
" 'ced': None,\n",
" 'ccg': 'NHS Milton Keynes',\n",
" 'nuts': 'Milton Keynes',\n",
" 'codes': {'admin_district': 'E06000042',\n",
" 'admin_county': 'E99999999',\n",
" 'admin_ward': 'E05009415',\n",
" 'parish': 'E04001275',\n",
" 'parliamentary_constituency': 'E14000822',\n",
" 'ccg': 'E38000107',\n",
" 'ccg_id': '04F',\n",
" 'ced': 'E99999999',\n",
" 'nuts': 'UKJ12',\n",
" 'lsoa': 'E01016820',\n",
" 'msoa': 'E02003475',\n",
" 'lau2': 'E05009415'},\n",
" 'distance': 92.95169474}]}"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"r=requests.get('https://api.postcodes.io/postcodes?lon={lon}&lat={lat}'.format(lon=lon,lat=lat))\n",
"r.json()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One thing to note from this response is that it actually returns *two* locations. We can write a simple bit of Python code to iterate through the results list and pull out the postcodes."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'MK7 6AA'"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#Test - can we retrieve the postcode from the first item (list index 0) in the results list?\n",
"r.json()['result'][0]['postcode']"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MK7 6AA\n",
"MK7 6AE\n"
]
}
],
"source": [
"#Iterate through all the results\n",
"for result in r.json()['result']:\n",
" print(result['postcode'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, let's create a function to display the postcode(s) associated with a supplied latitude, longitude pair."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"def get_postcode_from_latlon(lat, lon):\n",
" ''' Use the postodes.io API to retrieve postcodes for a location'''\n",
" \n",
" r=requests.get('https://api.postcodes.io/postcodes?lon={lon}&lat={lat}'.format(lon=lon,lat=lat))\n",
" \n",
" #Parse the JSON response\n",
" json = r.json()\n",
" \n",
" #Extract the results list\n",
" results = json['result']\n",
" \n",
" #Create an empty list to hold the postcodes\n",
" postcodes = []\n",
" \n",
" #Iterate through the results\n",
" for result in results:\n",
" #Extract the postcode\n",
" postcodes.append(result['postcode'])\n",
" \n",
" return postcodes"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['MK7 6AA', 'MK7 6AE']"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_postcode_from_latlon(lat, lon)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Summary\n",
"\n",
"In this notebook, you have learned how to geocode several different sorts of location identifer - postcodes, postal addresses and IP addresses.\n",
"\n",
"You have also seen how we can take the JSON data returned from the geolocation services and parse it as python dict that we can then start to work *as data*, for example, by plotting markers associated with identified locations on an interactive map."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.6"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment