Geocoding Singapore Coordinates: OneMap API | by Shawn Tham | Towards Data Science

Geocoding with Google API is the widely known way. However, it costs money once you finish your free credits. As a data analyst who does geospatial visualisation regularly with Tableau and Python, coordinates are very essential for plotting. In this article, I will demonstrate how to geocode Singapore coordinates using a free and accurate geocoding API developed by the Singapore Land Authority (SLA).
Step 1: Importing the relevant packages
import pandas as pdimport requests
Step 2: Read the dataset with pandas and examine it
df = pd.read_csv(r"C:\Users\shawn\Downloads\hdb-property-information\hdb-property-information.csv")df.head()
The dataset I am using is from Data.gov.sg and it contains the location of existing Housing Development Board (HDB) blocks, highest floor level, year of completion, type of building and number of HDB flats (breakdown by flat type) per block etc. I start by reading the data with pandas and taking a look at the first 5 rows to see which columns I can use. For demonstration purposes and making it as realistic as possible, I will not clean the data to show how you can still use OneMap API.
Step 3: Creating an address list that can be inputted into the API
df['Address'] = df['blk_no'] + " " + df['street']addresslist = list(df['Address'])
As there isn’t an address column, I concatenate the columns which contains the block number and the street name and save it into a new column. Geocoding requires some form of address so this step is necessary to proceed on. Next, I convert the newly created column into a list which can then be run through OneMap API.
Step 4: Writing a function for geocoding
def getcoordinates(address):    req = requests.get('https://developers.onemap.sg/commonapi/search?searchVal='+address+'&returnGeom=Y&getAddrDetails=Y&pageNum=1')    resultsdict = eval(req.text)    if len(resultsdict['results'])>0:        return resultsdict['results'][0]['LATITUDE'], resultsdict['results'][0]['LONGITUDE']    else:        pass
I start by defining the function as getcoordinates . Using the requests module, I will use it to search information on OneMap API. The address will have to be inserted as a string into the the given URL in order to retrieve information. Next, I use the eval() function to evaluate it as a Python expression. As not all addresses will have coordinates, I use an if else statement to only take those with results.
Step 5: Write a for loop with conditions
coordinateslist= []count = 0failed_count = 0for address in addresslist:    try:        if len(getcoordinates(address))>0:            count = count + 1            print('Extracting',count,'out of',len(addresslist),'addresses')            coordinateslist.append(getcoordinates(address))    except:        count = count + 1                   failed_count = failed_count + 1        print('Failed to extract',count,'out of',len(addresslist),'addresses')        coordinateslist.append(None)print('Total Number of Addresses With No Coordinates',failed_count)
Now, I start by creating an empty list to append the extracted coordinates. Next, I put a count function. By doing so, I am able to know where the progress is currently at and the total number of addresses that doesn’t have coordinates. This will allow me to keep track easily. Next, I input a try else condition which will allow the for loop to run continuously even if one of the addresses fail to retrieve any coordinates from the API. This is particularly useful to ensure the loop runs smoothly, especially for dirty datasets that are not able to generate any results from the API. In addition, you are still able to attain the bulk of coordinates. You could then filter for the remaining dirty addresses and clean those, making your life easier since you have less to clean!
Step 6: Join the attained coordinates back to the original DataFrame
df_coordinates = pd.DataFrame(coordinateslist)df_combined = df.join(df_coordinates)df_combined  = df_combined .rename(columns={0:'Latitude', 1:'Longitude'})
Lastly, I convert the list of coordinates into a DataFrame and join it to the original dataframe. As the index is still the same, it will join perfectly. I also renamed the column names as Latitude and Longitude to better reflect the data type in my dataframe.
Summary
Step 1: Importing the relevant packages
Step 2: Read the dataset with Pandas and examine it
Step 3: Creating an address list that can be inputted into the API
Step 4: Writing a function for geocoding
Step 5: Write a for loop with conditions
Step 6: Join the attained coordinates back to the original DataFrame
I hope you enjoy my article and that it convinced you to give this OneMap API a try! There are many great initiatives by the Singapore government to make datasets public and open. Thus, it is useful to explore and see what you can make out of it for data analytics projects. You can check out the full code on my Github here.