Simple Google Maps with Jekyll and JQuery

Google's latest version of their Mapping API is really great. Mainly, it's nice that I don't have to worry about API keys and can make very simple calls to get maps formatted exactly how I want.

I recently started putting events on my website and I wanted to include a simple map on the page. You could also change the code pretty easily to show many locations on a single map if you wanted. Once you start playing around with it, the sky is the limit.

To start, I created a new layout called event. The template will be able to make use of certain YAML attributes if it finds them. I'll get back to the template in a minute.

Once I know I have a particular type of post that uses an event template, I created some YAML front matter attributes on the post itself. Here's an example:

layout: event
location:
  address: 218 W 15th St, NY, NY
  latitude: 40.7396183
  longitude: -74.00017439999999

If you have the address, you can just use that value and skip the latitude and longitude. But if you don't have the address, but you have a particular set of coordinates, you can skip the address.

Now that your content is aware of a location, we need to send that to the event template that you created in your _layouts directory. I've created a javascript file that scans the page for elements that need to be mapped and then converts HTML to a map. So the first step is to create that HTML in my template. Here's the snippet of code I have in my event template.

{% if page.location %}
<div class="mapping">
  <div class="map-canvas" id="map-event"></div>
  <div class="map-point" address="{{ page.location.address }}" latitude="{{ page.location.latitude }}" longitude="{{ page.location.longitude }}">
    <div><b>{{ page.title }}</b></div>
    <div>{{ page.location.address }}</div>
  </div>
</div>{% endif %}

There are a few things going on here. First of all, everything should be wrapped in a DIV with a class of mapping. This allows me to have multiple maps on a page and denote that the points listed in the DIV correspond with a map. Next, I declare a DIV with a class of map-canvas. This will be the actual map that gets displayed. Be sure in your CSS to always specify a height for your map or it won't be visible. And then thirdly, I have one or more DIVs with a class of map-point. These will correspond to the markers on the map. The content within is what will be rendered in the pop-up info window when a user clicks on the map.

At this point, if you rendered the site, you would get an empty paragraph where the map should be. The information about the location should be showing up as attributes of that paragraph, but nothing is displayed to the end user yet. Now it's time to work Google Maps into the equation.

First of all, make sure that Google Maps is being loaded by putting this in your main layout file:

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>

Next, it's time to create your custom JQuery code to translate that HTML you created into an actual map. I created a file called (creatively enough) maps.js and put a reference to it in my main layout file.

The logic for displaying the map is roughly the following:

  • Find all occurrences of elements with a class of mapping
  • Loop through them and initialize elements with .map-canvas as a Google map
  • Find all the .map-point elements, add a marker to the map using the attributes of the HTML

Here's the entirety of that JavaScript file:

var geocoder; // currently not used

$(document).ready ( function() {

  //loop through all the mapping elements
  $(".mapping").each(function() {

    var map;
    var bounds;

    // initialize the map canvas
    $(this).find('.map-canvas').each(function() {    
      map = initMap(this);
      bounds = new google.maps.LatLngBounds();
    });

    // loop through map points and put them on the map
    var points = $(this).find(".map-point");
    points.each(function() {

      // create a latlong and marker for each point
      var latlng = new google.maps.LatLng($(this).attr('latitude'), $(this).attr('longitude'));    
      var marker = new google.maps.Marker({
        map: map,
        icon: $(this).attr('icon'),
        position: latlng,
        title: $(this).find(".description").text(),
        url: $(this).find(".description a").attr('href')
      });

      // if the map point has HTML, turn it into an info window
      var infowindow = new google.maps.InfoWindow({
        content: $(this).html()
      });

      // handle clicks to the marker
      google.maps.event.addListener(marker, 'click', function() {
        if (this.url) {
          window.open(this.url);
        } else {
          infowindow.open(map, marker);
        }
      });

      // if there are multiple markers, fit the map to them; otherwise, center the map on the only marker
      if (points.length == 1) {
        map.setCenter(latlng);
      } else {      
        bounds.extend(latlng);
        map.fitBounds(bounds);
      }

    });

  });

});


function initMap(canvas) {

  // set a default zoom unless it's specified
  zoom = parseInt($(canvas).attr('zoom')) || 15;

  // center the map and create options
  var latlng = new google.maps.LatLng(0, 0);
  var myOptions = {
    zoom: zoom,
    center: latlng,
    mapTypeControl: false,
    streetViewControl: false,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }

  // create the map
  map = new google.maps.Map(canvas, myOptions);
  map.setCenter(latlng);

  // set some custom styles
  map.set('styles', [
    {
      featureType: 'road',
      elementType: 'geometry',
      stylers: [
        { weight: 0.5 }
      ]
    }, {
      featureType: 'landscape',
      elementType: 'geometry',
      stylers: [
        { hue: '#ffff00' }
      ]
    }
  ]);

  return map;

}

Please note that there's a little extra functionality included here than I originally discussed. I created a second map that loads my latest Foursquare checkins. For those items, I am simply linking the marker to the URL of the venue of Foursquare. There's no info window for those items. Also, because I'm showing multiple items on a map, I have to go through a couple of lines of code to move the map to see all the items on it.

blog comments powered by Disqus
I've currently been building some pages in my Jekyll site for events. And with almost any event, you want to know who's coming. I decided to give Wufoo a try and use it to build an RSVP form on my site.
One of my big hurdles in moving to Jekyll was making sure that people that stumbled across old URLs for my site would get to the new content. Creating redirects in Jekyll turned out to be a snap.