Mapping Your Favorite Coffee Shop in the Philippines using Google Places API and Folium | by Francis Adrian Viernes | Towards Data Science

For most of us, coffee has become a non-negotiable in our morning routine.
But in a lot of urban planning studies, the number of coffee shops has become indicative of the level of development of a place or a city. It is not really surprising as coffee shops are avenues for business and client meetings and are found in business districts where busy employees need quick access to a coffee fix.
As such, just by knowing the number of coffee shops in a location, may tell us something valuable. But how do we proceed?
So now that we know the importance of this data, how then should we collect it?
Some coffee chains have a list of locations that are found on their websites and we can get them manually just by copying the info and geocoding the data ourselves.
But this would result in a tedious process that is prone to error and may take a longer time to clean.
This is where the magic of data science enters. For this tutorial, let’s use Google API to collect the data and Folium to visualize our data in HTML format.
Credits: We borrowed some codes from artemrys and modified it to fir out purpose for the Philippines’ cities and to return more than the count.
Analytics Project Design
The image above summarizes our process in dealing with this project. Note that this is only for the data gathering. I did not put here the cleaning and data visualization as they normally form part of a usual analytic project.
Preliminaries
#Preliminariesfrom collections import namedtupleimport requestsimport csvimport timeimport jsonimport osimport pandas as pd#For Interactive GeoVisualizationimport foliumfrom folium import Mapfrom folium.map import Layer, FeatureGroup, LayerControl, Markerfrom folium.plugins import MarkerCluster, FeatureGroupSubGroup, Fullscreen#To turn our DataFrame to a GeoDataFrameimport geopandas as gpdfrom shapely.geometry import Point, Polygon
Why Google API?
Between Google Maps API and OpenStreetMap, Google Maps API returns more updated data. This probably due to the fact that more resources are employed in Google and have more users. The caveat though is that after a number of calls, Google starts to charge so I advise developers to read up on the cost matrix and be aware while using the API.
For the purposes of this tutorial, assuming you have not used Google API for the month (the free tier refreshes monthly), this will cost you nothing.
When testing the code, limit your API calls to a few rows so you won’t run the error of running your free-tier out.
It goes without saying that in order to continue, one should register for a Google API key.
#the namedtuple method returns a new subclass of tuple with named fields#this is a convenient way of assigning names to tuples so you can later access them by nameCityCoords = namedtuple("CityCoords", ["city_name", "lat", "lng", "region"])# Google Maps Key# GMAPS_KEY = {Place your GMAPS Key here}#Google Place API Request#Note that we {lat}, {long}, {radius}, {keywords} and {key} will be arguments that we need to supply in laterG_PLACES_API = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={lat},{lng}&radius={radius}&keyword={keyword}&key={key}"#Token to skip to next pageG_PLACES_NEXT_PAGE_TOKEN = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?pagetoken={page_token}&key={key}"
You can as, early as now indicate the brands you wish to visualize. My four favorite coffee brands are Starbucks, Coffee Bean and Tea Leaf, Tim Hortons, and Coffee Project.
#Indicate brands you wish to collectcoffee_brands = ["Coffee Bean and Tea Leaf", "Starbucks", "Tim Hortons", "Coffee Project"]
Defining Functions
Let’s define the functions we will be calling on later. Recall that we have an analytical design to follow and we will write the functions that correspond to each step of the process.
This step is rather long so let’s not place them here. Instead, visit my Github page for the full code.
My page will contain the geocoded Philippines’ cities for all to use.
Cleaning the dataset
After running the codes to gather the data, it’s now time to clean our data.
The data we get from our API request may not entirely be clean. This particularly happens when the keywords you use match other brands’ keywords as well. Take, for instance, Coffee Bean and Tea Leaf where coffee may match other coffee shops as well.
The final count, as of October 2020, of our favorite coffee shops:
GeoVisualiztion through Folium
First, to use Folium, we have to turn our data frame into a GeoDataframe:
geometry = [Point(xy) for xy in zip(df['lng'], df['lat'])]#Let us create GeoDataFrame with df data and list of Point Geometriesgdf = gpd.GeoDataFrame(df, geometry=geometry)
We instantiate the Folium Map next:
#The folium.Map instantiates a folium map object with given parameterscoffee_map = folium.Map(location = [14.5540,120.9752],                       zoom_start=5, #from experience, this zoom level captures the whole PH                       tiles='CartoDB dark_matter',                       control_scale=True,                       prefer_canvas=True)#Add Fullscreen ControlFullscreen(            title="Full Screen",            title_cancel = "Exit fullscreen",            force_separate_button=True).add_to(coffee_map)
Before we proceed, we need to assign a different color per coffee brand so it will appear nice on the map:
#Let's Create a Color Dictionary for the Markerscolor_dict = {    "Starbucks": ' #00704A',    "Coffee Bean and Tea Leaf": '#362d26',    "Coffee Project": '#654321',    "Tim Hortons": '#dd0f2d'}
After that, let’s add the markers and clustering tool.
#Since we need to add a filter for this, we need to add them by brand:for brand in coffee_brands:    df_i = gdf[gdf['brand']==brand]    df_i.loc[:, "color"] = color_dict[brand]        #Let's add a marker feature - clustering    marker_cluster = MarkerCluster(control=False) #False so it will not appear as a layer    marker_cluster.add_to(coffee_map)    sub_group = FeatureGroupSubGroup(marker_cluster, name=brand, control=True, show=True)        #Popup Contents    for index, row in df_i.iterrows():                html = """                <h3>{title}</h3><br>        <b> {brand}</b><br>          {vicinity}<br>                """                popup_contents = folium.Html(html.format(title = df_i.loc[index, 'name'],                                                     brand = row.brand,                                                     vicinity = row.vicinity),                                         script = True)                popup = folium.Popup(popup_contents, max_width=2650)            folium.vector_layers.CircleMarker(radius = 8,                                                  location = (row.geometry.y,                                                              row.geometry.x),                                                  popup = popup,                                                  color = row.color,                                                  fill = True,                                                  fill_color = row.color,                                                  name = brand,                                                  control = True,                                                  overlay = True                                                 ).add_to(sub_group)        sub_group.add_to(coffee_map)
When we’re done, our Map will look like this:
Insights
From this map above, we can clearly see that most of our favorite brands of coffee are quite concentrated on the established business districts of the Philippines.
A caveat though is that there may be less popular coffee chains in the other parts of the Philippines and only the ones we like are found in the key business districts — Greater Manila, Cebu, Legazpi, Naga, and Davao City.
One can assume that the more coffee shops in a cluster, the more developed the business district is and we can therefore use this information to track which ones are developing quickly.
Let me know what you guys think!
Full code available at GitHub.