Map your locations with MapBox
On a recent project, I was tasked with getting a MapBox map integrated with one or more "Locations." For this project, a "location" is the physical location of one of the client's office buildings.
So I did a quick search to see if there were any existing modules that would provide my needed functionality. I found the MapBox module, which claims to provide a layer type to OpenLayers. Unfortunately, it doesn't seem to be compatible with the latest recommended release of OpenLayers.
My next stop was to see what MapBox provides in the way of an API. I found that they have a good JavaScript API that I could use.
So this post will show you how to use MapBox's JavaScript API to show "locations" on a map.
Prerequisites
The following modules are required:
- Views Datasource (tested with 7.x-1.x-dev) (and obviously Views itself)
- GMap (tested with 7.x-2.7)
- Location (tested with 7.x-3.0-rc2)
The first thing you'll need to do is download and install the required modules. On admin/modules, enable the following: GMap, Gmap Location, Location, Node Locations, and Views JSON.
Next, go to admin/config/content/location and check the box next to Use a Google Map to set latitude and longitude so your admins can set the longitude/latitude by clicking on a map.
You'll also want a location enabled content type. To do this, navigate to the edit screen of your content type (mine was called Location, but yours will vary) or create a new one.
Open up the Locative information tab. You'll need to set the minimum number of locations to 1 and the maximum number of locations to 1. Now on your node/add form, you should have a handy point & click map to set the longitude and latitude.
Create a basic map at MapBox
Even though we're going to use the API to place our markers, we still need a map to place them on. So, jump on over to mapbox.com and create an account. Once you've created an account, create a map to your liking, but don't add any markers. We'll do that from the Drupal side below. Once you're satisfied, click the publish button in the upper left corner of the window so we can get the map ID we need.
When you click the publish button, you'll get a popup that looks similar to this:
You can get the unique map ID on the mapbox.js tab. We'll need this ID in a bit.
Create a simple JSON web service using Views Datasource
The javascript (below) will use an AJAX call to fetch the location data. While it would be perfectly doable to implement to appropriate hooks in code, I chose to go the UI route on this.
Create a new View, and then create a Page display from the + Add button at the top. With this new page display selected, first set the page's path (under page settings). I set mine to /data/locations-json. You'll also want to set the pager to display all items. Now, configure the output format. Click Unformatted list in the Format section of the Views UI. Select JSON data document from the resulting popup.
Once you click OK to close the popup box, the corresponding settings popup will open. Set the form items as follows:
Next, we need to add some fields. At the bare minimum, you'll need Content: Title, Location: Latitude, and Location: Longitude. I also added Content: Image since I needed to display the location's image in the map's tooltip. Be sure to set the filter criteria appropriately. The Views UI should look similar to the following:
If everything is working correctly, you can browse to /data/locations-json to see your JSON formatted location data.
Create the map block in a custom module
The block we're creating is going to be a simple stub in a custom module. So in your custom module, add the following:
/**<br> * Implements hook_block_info().<br> */<br>function mymodule_block_info() {<br> $blocks = array();<br><br> $blocks['mymodule_location_map'] = array(<br> 'info' => t('Location Map'),<br> );<br><br> return $blocks;<br>}<br><br>/**<br> * Implements hook_block_view().<br> */<br>function mymodule_block_view($delta = '') {<br> $block = array();<br><br> global $user;<br> switch ($delta) {<br> case 'mymodule_location_map':<br> $block['subject'] = t('');<br> $block['content'] = mymodule_location_map();<br> break;<br> }<br><br> return $block;<br>}<br><br>function mymodule_location_map() {<br> drupal_add_css('http://api.tiles.mapbox.com/mapbox.js/v1.2.0/mapbox.css', 'external');<br> drupal_add_js('http://api.tiles.mapbox.com/mapbox.js/v1.2.0/mapbox.js', 'external');<br> drupal_add_js(drupal_get_path('module', 'mymodule') . '/js/map.js', 'file');<br> return '<div id="locations-map"></div>';<br>}
The first two functions just declare to Drupal that we one or more blocks. The third function, mymodule_location_map(), is where we'll put our map info.
The first two lines just link to MapBox's API library. We'll grab the javascript and some default styling. The third line loads our custom javascript (below), and the fourth lines gives us a container to load our map into.
Use the MapBox JavaScript API
The javascript to drive the map:
(function ($) {<br><br>Drupal.behaviors.locationMap = {<br> attach: function (context) {<br><br> var map = L.mapbox.map('locations-map', 'rob-chapterthree.map-5dupgsfx', {<br> scrollWheelZoom: false,<br> zoomControl: false,<br> minZoom: 2,<br> maxZoom: 2<br> }).setView({ lat: 40.00, lon: 0.00 }, 2);<br><br> $.ajax({ url: '/data/locations-json', dataType: 'json' }).done(function(data) {<br> // We need a GeoJSON object.type<br> var gj = [];<br> $(data.locations).each(function(idx, location) {<br> if (location.latitude && location.longitude) {<br> var item = {<br> type: 'Feature',<br> geometry: {<br> type: 'Point',<br> coordinates: [location.longitude, location.latitude]<br> },<br> properties: {<br> title: location.title,<br> image: location.image,<br> }<br> };<br> gj.push(item);<br> }<br> });<br><br> L.geoJson(gj, {<br> pointToLayer: function(feature, latlng) {<br> var marker = new L.Marker(latlng, {<br> icon: L.divIcon({<br> className: 'map-marker',<br> html: '<span class="dot"></span>'<br> }),<br> iconSize: [15, 15]<br> });<br><br> // Create custom popup content<br> var popupContent = '<img src="' + feature.properties.image + '">' +<br> '<h5>' + feature.properties.title + '</h5>';<br><br> marker.bindPopup(popupContent, {<br> closeButton: false<br> });<br><br> return marker;<br> }<br> }).addTo(map);<br><br> });<br> }<br>};<br><br>})(jQuery);
The first part of this javascript creates the map object, and sets some initial properties. There are two really important parts. The 'locations-map' corresponds to the HTML ID of the DIV container we created up in the mymodule_location_map() function of the module. The 'rob-chapterthree.map-5dupgsfx' is the ID of our map that we created over on mapbox.com. The other settings you can read up on mapbox.com.
The next chunk of javascript goes and gets our JSON data we created earlier. The main thing to understand from this section is that in order for the MapBox API to correctly parse the data, it needs to be in the GeoJSON format. So we convert our data into the proper format.
The remaining code just utilizes the MapBox API to create the markers for out locations. Here's the map in all it's glory:
Sweet!