Using CartoDB and Odyssey.js for Making Story Maps

Contact me at

This tutorial has been written so that it is generic enough for anyone to easily create a story map using a free CartoDB account and Odyssey.js. The tutorial is written specifically to support a course at Stanford University, but is being made publicly available for the benefit of all. We like the FOSS4G ethos, here, and try to live it.

I'll be maching up information from various sources (Wikipedia, etc...) on the modern history of tattooing in San Francisco. Mostly because it's interesting, and there is plenty of material that is cool to look at. That said, I'm not a historian and this is not an authoritative map of that history (now that I think of it, that would be cool though). It's a tutorial on how to make a Story Map from CartoDB and Odyssey.js.

In This Tutorial, You Will Learn To:

What You Will Need Before Getting Started

It Would Be Incredibly Helpful if You...

Getting Started with CartoDB

Create an Empty Map/Dataset

New Map Button

Create a new map

Create a new map, again

Create a new map, again

Adding Fields to an Empty CartoDB Table

Initial Map View You should now see something like the image, above, with a basic CartoDB Basemap and the tools you need to begin building your map. You should have already gone through the CartoDB Academy Basic courses, so we won't go through all of the details, now. We're going to get right to work creating fields to hold our content. Let's get started...

Create a new map, again

Add Columns

Add the following columns using the Add new column dialog box (just click the Add Column button again for each one):

Add Columns

Entering Your Data!

Now it's time to start putting some content into your CartoDB table. Hopefully, you've already assembled the materials you need and can simply begin to cut-&-paste that data into your table.

Add a Row

Editing Cell Values

Now all you need to do is repeat the process for all of your locations!

Geocoding Your Locations (making them show up on the map)

Once you have finished entering all of your data, you need to tell CartoDB that your 'latitude' & 'longitude' fields contain the geographic information about your locations. This will create geometry data in the default column, 'the-geom'.

GEO Button

Geocoding (selecting the columns)

You may have noticed that we could have simply placed street addresses into our table and 'geocoded' our data that way. We used coordinate for two reasons:


Map View

Ok. You've got a CartoDB map, now. We're not going to go into creating custom symbols, etc... since you can just go the the CartoDB Academy page (see the top of this page) and get all of that. We're just going to stick with the default symbol for this tutorial.

Customizing the Click & Hover Pop-ups

Now we want to do a little culstomization of the pop-ups that will appear when you click or hover on the points in your map. Notice that, right now, nothing happens when you hover over a point, and you get a message that "You haven’t selected any fields to be shown in the infowindow." when you click on a point. Now is time to fix that, but we're going to go beyond just showing the fields we've entered, since they are just URLs and not terribly interesting.

Boring default pop-up

infowindow customization

Header Selection

Change HTML Link

This should change the text in the pop-up header to the name value you put in the corresponding column.

    <img src="{{name}}"
    <img src="{{img_url1}}"

This should replace the current error message in the header with the image for which you placed the URL in your 'img_url1' column.

    <a href="{{link_url1}}" target="_blank"'>More Information</a>

Custom Pop-up

A Little More Work

You can customize your CartoDB map a bit more, now, using the Options button, etc... I suggest at least setting the options for Scroll Wheel Zoom.


That's it! We're now ready to switch gears to create a Story Map with Odyssey.js

You can find my Tattoo map at:

Creating a Story Map with Odyssey.js

Now that you've created the data for your story map, you're ready to go and build the narrative/navigation part of your application. We're going to use Odyssey.js for this. Odyssey.js is actually a JavaScript library that you can use on your own web server, but CartoDB has implemented a Sandbox that allows non-programmers to deploy a story map with very little coding and without any web server infrastructure, at all. Odyssey.js uses a very simple text matkup language called Markdown. This entire tutorial is actually written in Markdown, which allows Github to render the lists, HTML links and other elements.

Choosing an Odyssey.js Template

Create a Story

Slides Template

You should now see the Odyssey.js Sandbox template for a slideshow style story map. Note that you will be editing in the panel labeled Odyssey Sandbox and your edits will change the look of the content panel on the left, as well as the map. The Odyssey Sandbox panel will not be visible in the final story map.

Odyssey.js Slide Template Sandbox

Customize the config block of Your Odyssey.js Applicaiton

    -title: "Odyssey example FTW"
    -author: "CartoDB"
    -title: "The Tattoo Map of San Francisco"
    -author: "Stace Maples"

Bringing your CartoDB Visualization into Odyssey.js

The first thing we want to do is bring the CartoDB Visualization we created into the Odyssey.js application. We're going to use the vizjson option in the config block of your Odyssey.js markdown. First, you will need the JavaScript API URL of your visualization.

Share Button

Slides Template

    -title: "The Tattoo Map of San Francisco"
    -author: "Stace Maples"
    -vizjson: ""

Note that the data points from your CartoDB visualization should now be visible in your map.


Adding a Slide to Your Story Map

Now you're ready to start adding the content of your slides to the Oddysey.js Sandbox. Now is a good time to review a little bit about the Markdown language that drives the Odyssey.js application. As mentioned above, Markdown is a text markup language that acts a bit like shorthand for writing HTML code. Markdown simplifies the process of creating HTML text formatting, add ing images, links and other content to a page meant for the web. This entire tutorial is written in Markdown. Odyssey.js uses Markdown in thhe way it is intended (to format text and content) with one important exception. The H1 Heading element is used by Odyssey.js to signal the beginning of a "new slide." You can see this in the Odyssey.js Sandbox if you count the number of dots at the top of the content panel on the left, and then count the number of lines in the ODYSSEY SANDBOX panel on the right that begin with the # symbol. Same number! Each H1 Header line in the Sandobox define the begining of a new slide, as well as the Title text of that slide.

Here are a few more important bits of Markdown syntax:


# Level 1 (H1) Header, also indicates the beginning of a new slide in Odyssey.js
## Level 2 Header
### Level 3 Header

And will render like this:

Level 1 (H1) Header, also indicates the beginning of a new slide in Odyssey.js

Level 2 Header

Level 3 Header

Paragraph Text

Paragraph text is just paragraph text. You can **bold** text or use *italics*. Those are the most common uses and they end up looking like this:

Paragraph text is just paragraph text. You can bold text or use italics. Those are the most common uses and they end up looking like this.


1. This
2. Is
3. An
4. Ordered
5. List

* This
* Is
* A
* Bullet
* List

Which renders like this:
1. This
2. Is
3. An
4. Ordered
5. List


Create Links like this: [Google](

Which Renders like this: Google


You can place an image like this: 

![Stace is silly]( "Beardo")

Which will render like this:

Stace is silly

That's as far as we are going to dive into Markdown for this tutorial, but there is loads more you can do with it. If you want' to find out more, go to

Making Your First Odyssey.js Slide

Ok, time to create your first Story Map slide.

    #Lyle Tuttle's First Tattoo Shop

Add a New Slide

Add more slides!

Now that you know how, you can delete the Sandbox Markdown below your first slide and add all of the slides you want! Insert Images, etc...

Using a Georeferenced Map from as Your Basemap

It's possible to change the basemap of your Odyssey.js Story Map, either using the Basemap Selector at the bottom of the Sandbox panel, or by adding a bit of code to the config block. This is a bit tricky, but I will briefly explain it here.

Rumsey Map Finder


Affine Thumbnail

MapBox Embed Code


Tile Server URL

    -baseurl: "{z}/{x}/{y}.png"  

Sharing your Story Map

Once you have created all the slides you are interested in putting into your Story Map, you are ready to share your work with the world. The Odyssey.js Sandbox allows you to do this in three ways:

Sharing with an URL or Embed code

Odyssey.js can export your Story Map directly to a site called, which is just a platform for sharing and rendering JavaSript code, or give you an IFRAME code snippet to paste into your blog, or other website.

Share Story Map

Share Story Map

Here's the version (URL) of the tutorial map:

Here's the map in my blog:

Downloading and Hosting Your Story Map

You can also download the code for your Story Map and host it on your own Web Server (provided you have one).

Download Odyssey

Here's the map hosted on my web server:

Here is the Markdown Code for the Sample Story Map I Just Walked Through, Above:

-baseurl: "{z}/{x}/{y}.png"
-title: "The Tattoo Map of San Francisco"
-author: "Stace Maples"
-vizjson: ""
#The Tattoo Map of San Francisco
- center: [37.7687, -122.4355]
- zoom: 13
The History of American Tattooing and the City of San Francisco have an intimate relationship. This map explores that relationship. It is by no means authoritative, or comprehensive. Slides and information will be added, over time.

#Lyle Tuttle's First Tattoo Shop
- center: [37.7801, -122.4121]
- zoom: 16
![Lyle Tuttle](

Tuttle was born in Chariton, Iowa in 1931 but grew up in Ukiah, California. At the age of fourteen he purchased his first tattoo for $3.50. In 1949, he began tattooing professionally.[2] In 1954 he opened his own studio in San Francisco. This first shop was open for nearly 30 years. Tuttle tattooed Janis Joplin, Cher, Henry Fonda, Paul Stanley, Joan Baez, the Allman Brothers, and many other notable musicians, actors, and celebrities.

#Lyle Tuttle's Tattoo Museum and Studio
- center: [37.8024, -122.4135]
- zoom: 17
![Lyle Tuttle](

His first shop when working for Bert Grimm at 16 Cedar Way, Long Beach, CA. on "The Pike". After tattooing in Anchorage and Fairbanks, AK. and Oakland, CA., Lyle opened up shop in 1960 at #30 7th St., in between Mission St. and Market St., also referred to as South of Market, San Francisco, CA. As the story goes, the end of an era and the beginning of a new one. Lyle tattooed at #30 7th St., San Francisco, CA. for 29 and a half years, until the Loma Prieta Earthquake in 1989 caused the building to be "yellow tagged". The shop and the museum are both now open at 841 Columbus Avenue.

And, the code for the Custom HTML Popup in CartoDB:

<div class="cartodb-popup header with-image v2" data-cover="true">
  <a href="#close" class="cartodb-popup-close-button close">x</a>
  <div class="cartodb-popup-header">
    <div class="cover">
      <div id="spinner"></div>
      <div class="image_not_found"> <i></i> <a href="#/map" class="help">Non-valid picture URL</a></div>
      <span class="separator"></span>
      <h1 class="order1">{{name}}</h1>
      <div class="shadow"></div>
      <img src="{{img_url1}}" style="height:138px;display:inline" />
  <div class="cartodb-popup-content-wrapper">
    <div class="cartodb-popup-content">
      <a href="{{link_url1}}" target="_blank"'>More Information</a>
  <div class="cartodb-popup-tip-container"></div>