The world’s leading publication for data science, AI, and ML professionals.

Create Tailor-Made Tourist Maps with Folium

Jazz up your next vacation!

Photo by Kayla Duhon on Unsplash
Photo by Kayla Duhon on Unsplash

I’m a sucker for tacky tourist maps. You know the ones I’m talking about: those colorful, cartoony maps with 3-D renderings of castles, monuments, and the London Eye. Wouldn’t it be great to make a customized version for your own vacation? Well, hang on to your passport, because if you know Python, that’s just what we’re going to do!

In this Quick Success Data Science project, we’ll use the Folium library to make a customized tourist map for my Iceland vacation. We’ll use custom icons for volcanoes and waterfalls, overlay terrain and road maps, and mark the accommodation locations. Don’t fret if you’re never going to Iceland, because you can use this project as a template for your own trips.

Key Programming Subjects Covered

This project isn’t all fun and games. You’ll also learn a few useful geospatial techniques such as:

  1. How to stack base map tiles in Folium to show multiple feature types on the same map.
  2. How to use your own custom markers for features on the map.
  3. How to add static text to a Folium map and change its size and color (it’s not as straightforward as you might think).
  4. How to add an index map.

Folium

The open-source Folium library lets you visualize maps using Leaflet.JS, a powerful JavaScript library for building interactive web-Mapping applications on mobile and desktop platforms. First released in 2013, Folium is extremely popular, and you’ll find a wealth of material on the internet to help you learn how to use it and customize it to your needs.

With Folium, you can select tilesets from mapping services like OpenStreetMap, Mapbox, and Stamen. These tilesets are collections of raster or vector data broken up into a uniform grid of square tiles with up to 22 preset zoom levels. They let you produce beautiful leaflet maps with little effort.

To install Folium in a conda environment, use the following command:

conda install -c conda-forge folium

To install Folium with pip use:

pip install folium

For convenience, we’ll also use the open-source pandas library. You can install it with the following commands:

conda install pandas

or

pip install pandas

The Code

The following code was entered into Jupyter Notebook and is described by cell.

Importing Libraries

Folium is designed for dynamic, interactive mapping in web applications. However, for this project, we’re going to use it to make a static map that we can print on paper and shove in a backpack.

We’ll use Folium’s plugins module to make an index map that places the mapped area in a larger context. The DivIcon module will permit us to use custom icons for objects like waterfalls and volcanoes.

import pandas as pd
import folium
from folium import plugins
from folium.features import DivIcon

Loading the Volcano Database

Iceland could easily have been named, "Volcanoland," because it has around 130 volcanic mountains of which over 30 are classified as "active." I’ve compiled a CSV file of some of these with their names and coordinates and saved it as a Gist. For convenience, we’ll import this file as a pandas DataFrame.

volcano_df = pd.read_csv('https://bit.ly/458vXfu')
volcano_df.head(3)
The Volcano DataFrame (image by the author)
The Volcano DataFrame (image by the author)

Accommodations

It’s easy to lose track of time on a long vacation, so we’ll record the location of each night’s accommodation as "Day 1," "Day 2," and so on (you could easily use the actual date or something else). To generate a plottable database, I performed an online search for the latitude and longitude of each town and entered it as a Python dictionary.

accommodations = {'Days 1-3': (64.128288, -21.827774),
                  'Day 4':    (65.688492, -18.126169),
                  'Day 5':    (65.6000, -17.0000),
                  'Day 6':    (65.28333, -14.40139),
                  'Day 7':    (64.656888, -14.290051),
                  'Day 8':    (63.7833302, -18.0666664),
                  'Day 9':    (63.5445, -19.8),
                  'Day 10':   (64.258149, -20.514890),
                  'Day 11':   (64.80806, -22.80500)}

Waterfalls

Iceland could also be called "Waterfallland," as it has over 10,000 waterfalls. Fortunately, I only plan to visit six, so we’ll enter these as a Python dictionary, as we did for the accommodations.

waterfalls = {'Kolugjufur':     (65.3335, -20.5645),
              'Hengifoss':      (65.1, -14.9),
              'Svartifoss':     (64.023, -16.975),
              'Skogafoss':      (63.5245, -19.5083),
              'Seljalandsfoss': (63.6095, -19.989),
              'Gullfoss':       (64.3223, -20.1193)}

Custom Icons

Icons are visual representations of features on a map. A familiar example is the upside-down teardrop used to mark locations on Google Maps. For this project, we’ll use custom icons for volcanoes and waterfalls. For accommodations, we’ll use Folium’s standard circle marker.

Icons used in this project (image by the author)
Icons used in this project (image by the author)

To represent waterfalls, I used the royalty-free waterfall icon that came with my Microsoft 365 subscription. For volcanoes, I used an icon from ICONFINDER. I paid for this icon so it would be licensed for commercial use without attribution.

I can’t provide these particular icons to you, so you’ll have to supply your own. Just do an online search for "waterfall icons" and "volcano icons." Of course, you’ll want to be sure that these icons are properly licensed for whatever you intend to use them for.

Folium supports multiple file types, like PNG and JPG, and permits the scaling of icon images, so you don’t have to worry about the size of the icon. Save the icons in the same folder as your notebook or Python script.

Plotting the Map

The final notebook cell plots the map. At a high level, we’ll first pick a center point location for the map, then overlay tilesets at a predetermined zoom level. We’ll then loop through the volcano, waterfall, and accommodations databases and post the icons and corresponding names. We’ll finish by adding an index map. A more detailed explanation follows the code.

# Create basemap and overlay tiles:
center_location = [64.9, -18.6]
map = folium.Map(location=center_location, 
                 tiles='Stamen Terrain', 
                 control_scale=True,
                 zoom_start=7)
folium.raster_layers.TileLayer(tiles='Stamen Toner', opacity=0.3).add_to(map)

# Loop through volcano DataFrame and post volcano names and symbols:
for index, row in volcano_df.iterrows():
    volcano_icon = folium.features.CustomIcon('volcano_icon.png', 
                                              icon_size=(20, 20))

    folium.Marker(location=(row['Latitude'], row['Longitude']),
                  icon=volcano_icon).add_to(map)

    folium.map.Marker((row['Latitude'], row['Longitude']),
                      icon=DivIcon(
                          icon_size=(25, 25),
                          icon_anchor=(-5, 14),
                          html=f'<div style="font-size: 8pt">%s</div>' % str(
                               row['Volcano Name']))).add_to(map)

# Loop through waterfalls dictionary and post waterfall names and symbols:  
for key, value in waterfalls.items():
    waterfall_icon = folium.features.CustomIcon('waterfall_icon_dark_blue.jpg', 
                                                icon_size=(25, 25))

    folium.Marker(location=value,
                  fill_opacity=0.5,
                  icon=waterfall_icon).add_to(map)

    folium.map.Marker(location=value,
                      icon=DivIcon(
                          icon_size=(23, 23),
                          icon_anchor=(-10, 14),
                          html=f'<div style="color: blue">%s</div>' % str(key))
                     ).add_to(map)

# Loop through accommodations dictionary and post names and symbols:  
for key, value in accommodations.items():          
    folium.CircleMarker(location=value,
                        radius=8,    
                        color='red',
                        fill_color ='red',
                        fill_opacity=0.5).add_to(map)

    folium.map.Marker(location=value,
                      icon=DivIcon(
                          icon_size=(45, 30),
                          icon_anchor=(15, 9),
                          html=f'<div style="font-size: 8pt">%s</div>' % str(key)
                      )
                     ).add_to(map)

# Add index map:
mini_map = folium.plugins.MiniMap(toggle_display=True)
map.add_child(mini_map)

map  

The first step is to assign lat-lon coordinates for the map’s center point to a variable called center_location. Next, we call the Folium Map class and pass it the location, the tile to use, and a starting zoom level. The control_scale parameter activates the map’s scale bar.

While the Stamen Terrain tile includes roads, they are faint and hard to see, so we overlay the terrain tile with the darker Stamen Toner tile. By setting the toner tile’s opacity to a low value like 0.3, the terrain tile is only partially obscured.

To see additional map tiles available in Folium, check out this blog.

We next loop through the volcanoes DataFrame and post its contents. The folium.features.CustomIcon() method lets us use our custom volcano icon and control its size. The folium.Marker() class actually posts each volcano icon on the map.

Here’s the hard part. Folium is designed for dynamic, interactive web maps, so doing something as seemingly basic as posting static text is a little fiddly. We have to use the html argument and pass it f'<div style="font-size: 8pt">%s</div>' % str(row['Volcano Name']). We use this html format to not only post the name but to control its font size. Note how the text must be converted into a string before it can be plotted.

Plotting the waterfall and accommodation locations follows the same basic format. A difference is that we use the html argument to change the text color for waterfalls to blue.

We finish by adding an index map (mini_map). This map will dynamically reposition itself as you zoom in and out.

Here’s the final map:

The final tourist map (image by the author)
The final tourist map (image by the author)

An important consideration when making a tourist map is to tailor the icons and text to the final zoom level that you want to print. Ideally, this will be the zoom level passed to the zoom_start argument when calling folium.Map(). While Folium will let you zoom the map in and out, the relative size of the icons will change, and you may not be pleased with the results.

Printing the Map

As mentioned previously, Folium maps are meant to be used online. While it’s possible to save them as an image programmatically, it’s easier just to do a screen capture. You can then paste the result into a program like Microsoft PowerPoint or Publisher for additional embellishments before printing.

For busy areas where icons and/or text overlap, you can zoom in and print "local" maps. Note that you may want to use a new notebook cell to readjust the icon and text sizes for this new zoom level.

A zoomed, "local" map of the southern coast (image by the author)
A zoomed, "local" map of the southern coast (image by the author)

Summary

Tourist maps are a fun way to explore a new area. By combining Folium’s wonderful tilesets with your own custom icons and text, you can make personalized maps to both plan your trip and record your adventures.

Thanks!

Thanks for reading and please follow me for more Quick Success Data Science projects in the future.


Related Articles