Integrating WordPress and Google Maps API V.3
Here we will go through the steps for creating a Google Maps interface for a WordPress blog. Posts will appear as markers on the map, which you can click to reveal the post content in the Google Maps Infowindow.
Download a ZIP of the complete example.
Note, August 24 2013: In August 2013 Google updated the API to a new visual style that more closely matches their core products. One of the changes is that markers no longer display shadows. There may be other side effects as well, though as of this writing everything else seems to work properly. More investigation is warranted.
Also, a lot of people have trouble getting their markers to display. Often this has to do with the path to the image file. If you download the ZIP from Github, you will likely need to update the paths in map.js. Eventually I’ll revise the tutorial to address this, but in the meantime it’s something to keep an eye out for.
I’m starting with a fresh WordPress install, which at the time of this writing is v. 3.3.1. You may be modifying an existing theme, but for the sake of this tutorial, I’ll start a new one. But note that I’ll be doing only the minimum here to create a working demo, omitting much that is necessary for an actual, standards-compliant and useful website.
Add Location Data to Posts
Update: I’ve published the Address Geocoder plugin for WordPress that simplifies the process of adding location data to posts. I recommend using it instead of the custom fields technique outlined here. After installing it, skip to the next section, “Initialize Custom Theme,” below. Your code will need to be modified in three places, as noted below.
In order to know where to place a marker on a map, the Google Maps API needs to be supplied with latitude and longitude coordinates in the format of a LatLng object. We’re going to include this as a Custom Field with our posts.
Navigate to the Post Edit page and scroll down to the Custom Fields box. (If you don’t see it, you may need to turn it on via the Screen Options button at the top of the page.)
Name the custom field “latlng.” Now, there are various geocoding services on the web that allow you convert an address to latlng (including the Google Maps API itself, but that’s another topic). For this example we’ll use the White House. Go to Geocoder.us and enter “1600 Pennsylvania Avenue NW, Washington DC”. It will spit out a number in brackets such as (38.898748, -77.037684). Use that as the value and press Add Custom Field.
If you like, you can optionally create a few more posts and geocode them with locations in Washington DC.
Initialize a Custom Theme
In the wp-content/themes directory, create a new folder; mine will be called mapdemo. In this new folder, create a blank file called style.css and one called index.php; these are the minimum needed to created a WordPress theme. Also create a file called map.js.
To make our theme recognizable by WordPress we will place what’s called a theme header into our style.css. The very minimum header is as follows:
/*
Theme Name: Map Demo
*/ |
You can now go ahead and activate your theme using the WordPress dashboard (Appearance -> Themes). But if you load your blog at this point you will see that it’s totally blank. Let’s put in the bare-bones of an HTML page, with a couple of functions required by WordPress. Put the following into index.php:
<html> <head> <title>Maps Test</title> <?php wp_head(); ?> </head> <body> <?php wp_footer(); ?> </body> </html> |
Link the JavaScript Files
Our map will be created using two JavaScript files: The Google Maps API library and our own maps.js. We do this in the head section of index.php, after the title tag. To include the API we will link directly to the file on Google’s server:
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> |
And to include maps.js we will do something similar, but you’ll notice that we use WordPress’ get_stylesheet_directory_uri() function to dynamically write the path to the file in our theme directory:
<script type="text/javascript" src="<?php echo get_stylesheet_directory_uri() ?>/map.js"></script> |
Create the Map
WordPress uses something called The Loop to display posts. Let’s get that started by testing if, given the parameters passed via the URL, WordPress has any posts to display. In index.php, after the opening body tag, write:
<?php if ( have_posts() ) : ?> <!-- WordPress has found matching posts --> <?php else : ?> <!-- No matching posts, show an error --> <h1>Error 404 — Page not found.</h1> <?php endif; ?> |
Note: If you are interested in using this with a custom post type, depending on the context your loop may look different. Perry at Point At The Moon has posted a follow-up tutorial that includes custom post types.
Next create a DIV element that will be the container for our map; we’ll tell the API its name and Google will fill it with magic! We’ll use CSS to make it fill the window. After the line with the “WordPress has found matching posts” comment, write:
<div id="map" style="width: 100%; height: 100%;"></div> |
Now open up maps.js. We’re going to create a JavaScript function that loads in a map with a few settings, which I won’t go into the details of here, but are well documented. Write:
function initialize() { map = new google.maps.Map(document.getElementById('map'), { zoom: 12, center: new google.maps.LatLng(38.898748, -77.037684), mapTypeId: google.maps.MapTypeId.ROADMAP }); } |
Note: I’ve created a follow-up post on centering the map on the latest post.
But, like all functions, it’s not enough to define it — it must also be called. We’ll do this by modifying the body tag of index.php, like so:
<body onload="initialize()"> |
Reload your page and you should now see a map filling the window, centred on Washington DC.
Place the Markers
The way we’re going to get our markers onto the map is to:
- create a list (an array) of locations;
- specify the images we’re going to place at those locations;
- then go through (iterate) through the list, placing each one.
First up, the locations. We’re going to finish up that WordPress loop we started, where for each post we output an item in an array called “locations.” At this point we’re going to output only our “latlng” custom field, not the post title or content or any of the others. In index.php, right above the div with the ID of “map”, write this:
<script type="text/javascript"> var locations = [ <?php $i = 1; while ( have_posts() ) : the_post(); ?> <?php if ( get_post_meta($post->ID, 'latlng', true) !== '' ) : ?> { latlng : new google.maps.LatLng<?php echo get_post_meta($post->ID, 'latlng', true); ?>, }, <?php endif; ?> <?php $i++; endwhile; ?> ]; </script> |
Note: If you are using the Address Geocoder plugin, here are the first two modifications you need to make. Change the two instances of get_post_meta($post->ID, ‘latlng’, true) to get_geocode_latlng($post->ID).
Next we’ll define the images we want to use as markers. Each marker has two parts — the marker itself and its shadow, and for each you will supply paths to images of your choosing; I’ve included a couple in the ZIP, linked above, or you can use your own. Upload the images into your theme folder. At the very top of maps.js, above the initialize function, write:
var pinkmarker = new google.maps.MarkerImage('/wp-content/themes/mapdemo/pink_Marker.png', new google.maps.Size(20, 34) ); var shadow = new google.maps.MarkerImage('/wp-content/themes/mapdemo/shadow.png', new google.maps.Size(37, 34) ); |
If you are using your own images you will want to check Google’s documentation of the MarkerImage options to see what each part of these lines mean, so that you can customize them.
Note: I’ve created a follow-up post on using the post’s Featured Image as the marker.
And the final step is to loop through the locations, placing a marker for each onto the map. In maps.js, within the “initialize” function, after the “var map…” block, write:
for (var i = 0; i < locations.length; i++) { var marker = new google.maps.Marker({ position: locations[i].latlng, icon: pinkmarker, shadow: shadow, map: map }); } |
Reload your page and you should now see your marker(s) placed on the map.
Enable the Infowindow
Our last task is to create a Google Maps “infowindow” which will pop-up when a marker is clicked, showing the title and content of the post. We will do this by:
- outputting the content into a hidden div;
- referencing that div in our array of locations;
- and adding what’s known as a “click listener” to our JavaScript function to open the window.
First up, the content. This will be a a second “while” loop, immediately before the one we wrote previously. It will output a series of DIVs, each with a unique ID and containing content to be shown in the infowindow. In index.php, directly following the “matching posts” comment line, write:
<div style="display: none;"> <?php $i = 1; ?> <?php while ( have_posts() ) : the_post(); ?> <?php if ( get_post_meta($post->ID, 'latlng', true) !== '' ) : ?> <div id="item<?php echo $i; ?>"> <p><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></p> <?php the_content(); ?> </div> <?php endif; ?> <?php $i++; ?> <?php endwhile; ?> </div> |
Note: If you are using the Address Geocoder plugin, here’s the third modification you need to make. Change get_post_meta($post->ID, ‘latlng’, true) to get_geocode_latlng($post->ID).
Next add a reference to these DIVs to our locations object. Directly after this line:
latlng : new google.maps.LatLng<?php echo get_post_meta($post->ID, 'latlng', true); ?>, |
Add this:
info : document.getElementById('item<?php echo $i; ?>') |
Now define the infowindow (documentation at Google). Switch over to maps.js and, at the very top, write this:
var infowindow = new google.maps.InfoWindow(); |
Finally, add a click listener for each marker. Within the “for…” block of the “initialize” function, after the “var marker…” block, write:
google.maps.event.addListener(marker, 'click', (function(marker, i) { return function() { infowindow.setContent(locations[i].info); infowindow.open(map, marker); } })(marker, i)); |
And that’s it! Reload the page and you will now be able to click the marker(s), opening up an infowindow containing the post content. Mission accomplished!
Posted February 2012