This tutorial is out of date! Try my longer, updated Flask-SQLite webapp tutorial here

Adding maps to our Flask webapp

Now that we’ve geocoded our schools, time to make some maps! We’ll start by making a map on the school page, then level up to a map on the main page.

Slippy maps and Leaflet.js

The kind of map we’re using is a slippy map, which means it’s a map that works like Google Maps. You can drag and zoom, mainly, which makes it unlike most d3 maps.

Since we aren’t going to code a slippy map from scratch, the best library is (I’d say) Leaflet.js. It’s simple to use and works with a lot of different other… map things.

To use leaflet, we need to add two lines to our layout.html. The first is the CSS, and the second is the JavaScript.

 <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
 <script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>

We’ll put both of those somewhere before the end of our <head> element.

Creating our map

Now that every page it charged up with Leaflet, it’s time to put it to use! We’re going to edit our show.html.

First we need to give Leaflet a place to put the map, and give that map a height (you always need a height). We’ll just make a div with the id of map, and give it a height with inline style.

<div id="map" style="height: 250px;"></div>

Now we need to create a map. This takes two steps: first, initialize the map, and then to draw the basemap.

We’re going to use a CartoDB basemap, because CartoDB is the best thing since snickerdoodles.

This goes in show.html underneath your map div.

<script type="text/javascript">
  // The first parameter are the coordinates of the center of the map
  // The second parameter is the zoom level
  var map = L.map('map').setView([40.712, -74.006], 11);
  
  // {s}, {z}, {x} and {y} are placeholders for map tiles
  // {x} and {y} are the x/y of where you are on the map
  // {z} is the zoom level
  // {s} is the subdomain of cartodb
    var layer = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>'
  });
  
  // Now add the layer onto the map
  map.addLayer(layer);
</script>

Customizing our map

Let’s say we want to add a marker where our school is. If we wanted to add it to, I dunno, [40.712, -74.006], we could add something like this

// It even opens up a popup when you click it!
L.marker([40.712, -74.006]).addTo(map)
  .bindPopup("<strong>Henry Street School</strong>").openPopup();

Notice how both the layer before was added to the map - map.addLayer(layer) - while this marker was added with .addTo(map). This is what we call “flexibility that might sometimes be confusing.” You can use either.

But back to the topic at hand: hey, where’s our school? Not there. That’s just the center of New York City!

Since we geocoded our schools and added latitude and longitude columns, we can now access that using school.latitude and school.longitude. And since we’re inside of our template, we just wrap them in `` tags and it will plop them right now.

Let’s replace the marker’s latitude and longitude with our own.

L.marker([{{ school.latitude }}, {{ school.longitude }}]).addTo(map)
  .bindPopup("<strong>Henry Street School</strong>").openPopup();

You might initially scream “but that isn’t JavaScript!” and you’d be completely right. What’s happening takes a couple steps

  1. First, in the backend, all of the template blanks (i.e. {{ }}) are filled in. After this happens, {{ school.latitude }} becomes something like 40.701.
  2. The page is sent to the browser, and *then the JavaScript is run. So by the time Chrome sees it, all of those {{ }} have been replaced with actual values. Only Flask sees the {{ }} pieces!

Now, if we want to be rather insecure about this, we could do the following.

L.marker([{{ school.latitude }}, {{ school.longitude }}]).addTo(map)
  .bindPopup("<strong>{{ school.school_name }}</strong>").openPopup();

Why is this bad? Why is this not bad? Why is this possibly bad?

To find out, play Google’s cross-site scripting game.

Want to hear when I release new things?
My infrequent and sporadic newsletter can help with that.