Advertisement

Getting Creative With the Google Maps API

by

You’ve designed a shiny new website; carefully selecting the colors, typography and photographs to perfectly reflect the company's branding.  Then your client asks you to add a map. Sure, you could use a map building ‘wizard’, such as the one that comes with every google account. But, let’s face it, their functionality is limited and they look pretty generic!

The Google maps API, on the other hand, gives you the freedom to create completely customized maps, which can do all sorts of cool things. The Portsmouth History map is a site I built recently using this API.

Map I created using the Google maps API.
Portsmouth history map I created using the Google maps API. 

This series of tutorials will show you how to create customized maps using the Google maps API. It will involve getting your hands dirty with a bit of JavaScript, but it’ll be worth it.

The tutorials will cover all sorts of things. To name a few: custom map colors, menus and markers; bringing your own map design (e.g. a handrawn map) to life by overlaying it onto a zoomable google map; linking up to the Flickr API; image optimization; responsive design; code optimization and validation.  Or, in other words, by the time you're done, not only will you be able to create lovely maps, you’ll also have looked at many things relevant to building any website.

Scenario

These tutorials use the scenario of a UK company promoting music festivals. The "Final Product" picture above shows the kind of thing we’re aiming for. Take a look at the demo.

Note: Only the Glastonbury marker is 'active' in this example.

Before starting, you may like to download the files associated with this tutorial.  I've included a separate html file for each activity.


What is an API Anyway?

An API, or Application Programming Interface, is a fancy way of saying a set of commands (e.g. functions) a company (e.g. Facebook, Twitter, You Tube, Google) publish.  The idea is that you can use these commands to create a highly customized version of their content.  For example, in the case of the Google Maps API the 'content' is the maps, whereas in the case of the Flickr API the 'content' is the photos.

When people talk about ‘mash-ups’ they mean they've used the API of two or more companies to combine content, for example making pictures from Flickr appear on a Google map. There are literally thousands of these APIs around; take a look at Programmable Web for more info.

We’ll look at the Flickr API in a later tutorial, but to start with we’ll stay focused on the Google Maps API. This will let you do all sorts of things, including customizing the colors, map markers, pop-up box style, level of detail and zoom level. Not to mention actually making the map do some cool things, such as displaying live data, grouping markers, planning routes, drawing custom overlays, dynamically revealing data .... the list is endless!


Getting Started With Google Maps

At the risk of pointing out the obvious, to get started you need a Google account. If you don't currently have one, head over to Google and signup.

Armed with your Google account, you can now venture into Google maps API pages.  Bookmark this page; you’ll become very familiar with it. The main areas are:-

  • Developer's Guide:  Code snippets that show you how to use the API functions to do certain things.
  • API reference: The full reference list of all the functions in the API. Use this as a kind of 'shopping list' of what you can do using the API. (But bear in mind there are additional libraries that let you do even more. We’ll get to these later).

Before you go any further, you also need an API key (a long sequence of letters and numbers, unique to you). Get your API key here.

google-maps-api-key

Creating Your First Map

OK, you're ready to start building.

google-maps-api-activity3
Download source files or check out the live version

To make the provided map work, replace where it says ‘YOUR_API_KEY_GOES_HERE’ with your own API key. Your map should resemble the map shown above.

The basic principal of the code is that it creates a div (called festival-map) into which the JavaScript loads the map.  I’ve added comments in the code to explain how it works, but it’s worth highlighting the main bits.

The code first declares your application as HTML5 using the Doctype declaration <!DOCTYPE html>.

It then sets up the styling for the map; the bits between the <style type="text/css">  and </style>  tags.  You can adapt the styling on the #festival-map id depending on where you want to put your map. If you want a 'full screen' map, then set the width and height to 100 per cent and remove the margins.

Then, skipping over the JavaScript for a moment, the code between the body tags sets up an empty html object, i.e. a div (festival-map) to hold the map. This acts as a kind of 'place holder', into which the JavaScript loads the map.

<div id="festival-map"></div>

OK - now looking at the JavaScript - the code first connects to your google maps API key.

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY_GOES_HERE&sensor=true"></script>

It then launches into the JavaScript that creates the map. Quick reminder:  JavaScript functions (e.g. loadFestivalMap) are a set of commands that only run when they are called by some other code. This may look complicated, but it’s actually only doing a few things -

  • The configuration options (festivalMapOptions) of the map (e.g. the latitude and longitude, zoom levels etc) are set, and an empty JavaScript variable (festivalMap) is created which the map will be assigned to shortly. (Tip: itouchmap is a handy site for looking up latitude and longitude coordinates of any location.)
  • Then the loadFestivalMap function is triggered to run by this line:google.maps.event.addDomListener(window, 'load', loadFestivalMap);
  • This function creates the map, assigning it to the JavaScript variable created for it (ie festivalMap). While doing this, it also applies the configuration options and loads the map into the festival-map div created to hold it.
  • Finally, the loadMapMarkers function is triggered into running by the last line of the loadFestivalMap function.

If this makes no sense, take a read through the comments within the full version of the code as they explain it further.


Images as the Map Markers

By now you should have a map up and running, and with any luck you haven't been too baffled by the JavaScript! Assuming all is well, we can start making the map look a bit more interesting.

google-maps-api-activity4

It’s easy to use your own images as the map markers. In a similar way to how you set properties options for the map itself (e.g. center, zoom etc. as we did above), you can also alter the properties of the map markers. To use a different image as the map marker, you just need to set the icon property of your map markers. (If you are interested, the google maps API - markers section lists all the map marker properties, methods and events that you can use).

Typically the map markers are .pngs because they allow you to have an irregular shape with a transparent background.

We also need to set the ‘clickable’ area for each marker to ensure the ‘image’ bit of the .png is clickable and the transparent bit isn’t. This is really important if the markers overlap.

You first create the map icon image (markerIconGlastonbury), and then create the map icon shape (markerShapeGlastonbury), and finally you then link both of these to your map marker (markerGlastonbury).

//Setting the position of the Glastonbury map marker.
var markerPositionGlastonbury = new google.maps.LatLng(51.159803, -2.585585);

//Setting the icon to be used with the Glastonbury map marker.
var markerIconGlastonbury = {
 url: 'icons/icon_glas_uk.png',
 //The size image file.
 size: new google.maps.Size(225, 120),
 //The point on the image to measure the anchor from. 0, 0 is the top left.
 origin: new google.maps.Point(0, 0),
 //The x y coordinates of the anchor point on the marker. e.g. If your map marker was a drawing pin then the anchor would be the tip of the pin.
 anchor: new google.maps.Point(189, 116)
};

//Setting the shape to be used with the Glastonbury map marker.
var markerShapeGlastonbury = {
 coord: [12,4,216,22,212,74,157,70,184,111,125,67,6,56],
 type: 'poly'
};

//Creating the Glastonbury map marker.
markerGlastonbury = new google.maps.Marker({
 //uses the position set above.
 position: markerPositionGlastonbury,
 //adds the marker to the map.
 map: festivalMap,
 title: 'Glastonbury Festival',
 //assigns the icon image set above to the marker.
 icon: markerIconGlastonbury,
 //assigns the icon shape set above to the marker.
 shape: markerShapeGlastonbury,
 //sets the z-index of the map marker.
 zIndex:102
});

You can download the full html file from the top of this page, or take a look at the live version. For the moment, I’ve just added one map marker…obviously I’ll add more shortly!

Hint: To get the map icon shape coordinates, I inset the icon image into a temporary web page in Dreamweaver, and then use the Polygon Hotspot Tool to get the coordinates, and then copy these coordinates back into my main map page.

A Quick Note About Z-indexes

Elements, such as  map markers, can have both x, y and z coordinates.  The z-index is the depth.  It determines how elements are ‘stacked’ on top of each other, and hence how they overlap.

If the x and y coordinates of an element are the same, then elements with higher z-indexes are displayed on top of elements with lower z-indexes.  (Z-index only works on positioned elements, e.g. relative, fixed or absolute).

So far the map looks OK because by default the Google maps API gives elements appearing lower down the screen a higher z-index, so they appear on top of items slightly further up the screen. E.g. the Isle of Wight icon is on top of the Glastonbury and the Reading festival icons (see picture below).

Later in this tutorial we’ll take a look at how you can change the z-indexes to manipulate how the map markers are displayed.


Changing the Color and Level of Detail

Though the map is starting to take shape, the default map doesn't really fit with the overall effect I want to achieve. Fortunately, it’s really easy using the API ‘style’ options to customize how the map looks.

I should also say at this point that I just grabbed a few music festivals to illustrate this example. This was somewhat random, and my selection doesn't reflect my opinion - either for or against - any festivals!

google-maps-api-activity6

Step 1

Use the JSON wizard to workout how you want your map to look. You can change the colors of pretty much everything, add and remove detail, set the color fill and geometry independently etc.  It's worth spending a bit of time getting used to how this works.  (In case you are wondering, JSON is just a way of formatting information so a computer can read it.)

Step 2

When you are happy with the style shown in the JSON wizard preview, click the Show JSON button and copy the code.

Step 3

Go back to your html document, add a variable just after the opening JavaScript tag to hold this information, then paste in the code.


//First, we read in the data describing style.
var style_festival = [
 {
 "featureType": "administrative",
 "stylers": [
 { "visibility": "off" }
 ]
 },{
 "featureType": "poi",
 "stylers": [
 { "visibility": "off" }
 ]
 },{
 "featureType": "transit",
 "stylers": [
 { "visibility": "off" }
 ]
 },{
 "featureType": "road",
 "stylers": [
 { "visibility": "off" }
 ]
 },{
 "featureType": "landscape",
 "stylers": [
 { "color": "#FFE200" }
 ]
 },{
 "featureType": "water",
 "stylers": [
 { "visibility": "on" },
 { "color": "#4f92c6" }
 ]
 }
];

Step 4

You need to get your map to treat the information you’ve just added as a map style. Use StyledMapType to do this.

//Then we use this data to create the styles.
var styled_festival = new google.maps.StyledMapType(style_festival, {name: "Festival style"});

Step 5

Finally, you need to assign your new style to your map to 'activate' the style.

festivalMap.mapTypes.set('map_styles_festival', styled_festival);
festivalMap.setMapTypeId('map_styles_festival');

Please use the link at the start of this tutorial to download a copy of the code for the map so far, or take a look at the live demo for this part of the tutorial. I’ve actually added two styles. One is activated straight away, and the other will be activated when the map is zoomed to give a greater level of detail. (This is explained further below.)

I also couldn't resist adding a background at this point :) Take a look at the body css selector to see how I did this.


Pop up Boxes and Dynamic Zoom

google-maps-api-activity7

Okay, it’s now time to make the map actually do something! You could use the built-in Info Window overlay, but they don't look fantastic and they cannot easily be customized.  So, instead, we are going to use the Infobox library.

Step 1

Download a copy of the Infobox library. Unzip it, and store it in a folder close to your map. Then add this line near the top of your html file.


<script src="infobox/infobox.js" type="text/javascript"></script>

Step 2

Add z-indexes for each map marker so that the ones nearer the bottom of the screen appear on top of the ones higher up. (i.e. the closer to the bottom of the screen a marker is, the higher its z-index should be.) All will be revealed later as to why you are doing this! For example -


//Creating the Glastonbury map marker.
markerGlastonbury = new google.maps.Marker({
 //uses the position set above.
 position: markerPositionGlastonbury,
 //adds the marker to the map.
 map: festivalMap,
 title: 'Glastonbury Festival',
 //assigns the icon image set above to the marker.
 icon: markerIconGlastonbury,
 //assigns the icon shape set above to the marker.
 shape: markerShapeGlastonbury,
 //sets the z-index of the map marker.
 zIndex:107
});

Step 3

Add the following code after each map marker. Please read through the comments within the code to see what it's doing.


//Creates the information to go in the pop-up info box.
var boxTextGlastonbury = document.createElement("div");
boxTextGlastonbury.style.cssText = pop_up_info;
boxTextGlastonbury.innerHTML = '<span class="pop_up_box_text"><img src="content/glastonbury.jpg" width="400" height="285" border="0" /></span>';

//Sets up the configuration options of the pop-up info box.
var infoboxOptionsGlastonbury = {
 content: boxTextGlastonbury
 ,disableAutoPan: false
 ,maxWidth: 0
 ,pixelOffset: new google.maps.Size(-241, 0)
 ,zIndex: null
 ,boxStyle: {
 background: "url('infobox/pop_up_box_top_arrow.png') no-repeat"
 ,opacity: 1
 ,width: "430px"
 }
 ,closeBoxMargin: "10px 2px 2px 2px"
 ,closeBoxURL: "icons/button_close.png"
 ,infoBoxClearance: new google.maps.Size(1, 1)
 ,isHidden: false
 ,pane: "floatPane"
 ,enableEventPropagation: false
};

//Creates the pop-up infobox for Glastonbury, adding the configuration options set above.
infoboxGlastonbury = new InfoBox(infoboxOptionsGlastonbury);

//Add an 'event listener' to the Glastonbury map marker to listen out for when it is clicked.
google.maps.event.addListener(markerGlastonbury, "click", function (e) {
 //Open the Glastonbury info box.
 infoboxGlastonbury.open(festivalMap, this);
 //Changes the z-index property of the marker to make the marker appear on top of other markers.
 this.setZIndex(google.maps.Marker.MAX_ZINDEX + 1);
 //Zooms the map.
 setZoomWhenMarkerClicked();
 //Sets the Glastonbury marker to be the center of the map.
 festivalMap.setCenter(markerGlastonbury.getPosition());
});

Step 4

In the code above, you called the function setZoomWhenMarkerClicked. This will zoom the map in when a person clicks on a marker.  But not an awful lot will happen until you’ve created this function! This is the function you need to create -

function setZoomWhenMarkerClicked(){
var currentZoom = festivalMap.getZoom();
 if (currentZoom < 7){
 festivalMap.setZoom(7);
 }
}

Step 5

As the map zooms in, the chances are you want to show more detail on your map. This is done by adding an event listener and using the getZoom method to continuously check whether the zoom has been changed. If it has been increased (above level 6), the setMapTypeId  method is used to set the second (more detailed) style defined above.

//Continuously listens out for when the zoom level changes. This includs when the map zooms when a marker is clicked.
google.maps.event.addListener(festivalMap, "zoom_changed", function() {
 var newZoom = festivalMap.getZoom();
 //If the map is zoomed in, the switch to the style that shows the higher level of detail.
 if (newZoom > 6){
 festivalMap.setMapTypeId('map_styles_festival_zoomed');
 }
 //Otherwise the map must be zoomed out, so use the style with the lower level of detail.
 else {
 festivalMap.setMapTypeId('map_styles_festival');
 }

});

Step 6

Style your infobox using the css to make it look pretty.

//Variable containing the style for the pop-up infobox.
var pop_up_info = "border: 0px solid black; background-color: #ffffff; padding:15px; margin-top: 8px; border-radius:10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; box-shadow: 1px 1px #888;";

Step 7

When an infobox has been opened, the map marker is brought to the front. However, if you close the infobox and then zoom the map out to it’s original position, the marker remains in front. This can look weird.

In Step 1 you set the z-index for each marker. You can use this now to fix this problem. You need to create a function (ie. resetZindexes) that resets the z-indexes to their original values, and then add a call to this function (i.e. resetZindexes();) to infobox.js to trigger the function to run when the infobox window is closed.

Go to the link at the top of this page for a copy of the code for the site so far, or take a look at the live version of how it should look by now.  I’ve only added the pop-up for Glastonbury. Feel free to add the others yourself!


Customized Banners and Navigation

google-maps-api-activity8

We are almost there with customizing the look of the map, but before we call it a day we should add a few navigation controls.

Google maps has 12 areas where you can add map controls, banners etc.  You can completely customize these areas, adding whatever html etc. you want.

We are going to add a custom banner and navigation on the right hand side of the map.

Step 1

To get started, first define a function to create the menu. This code extract is rather long…

//Function that creates the control panel area, ie. the map title and the 2 buttons just beneath it.
function createControlPanel (controlPanelDiv){
 controlPanelDiv.style.padding = '0px';
 controlUI = document.createElement('div');
 controlUI.style.border='0px solid white';
 controlUI.style.margin='10px';
 controlUI.style.paddingTop='11px';
 controlUI.style.paddingBottom='5px';
 controlUI.style.paddingLeft='0px';
 controlUI.style.paddingRight='0px';
 controlUI.style.width='245px';
 controlUI.style.height='419px';
 controlPanelDiv.appendChild(controlUI);

 //Map title
 titleBar = document.createElement('div');
 titleBar.style.backgroundColor = '#89CBED';
 titleBar.style.height='255px';
 titleBar.style.width='245px';
 titleBar.style.marginTop='0px';
 titleBar.style.marginBottom='0px';
 titleBar.style.marginLeft='0px';
 titleBar.style.marginRight='0px';
 titleBar.style.paddingTop='6px';
 titleBar.style.paddingBottom='2px';
 titleBar.style.paddingLeft='0px';
 titleBar.style.paddingRight='0px';
 titleBar.style.borderTopLeftRadius='5px';
 titleBar.style.borderTopRightRadius='5px';
 titleBar.style.borderBottomLeftRadius='0px';
 titleBar.style.borderBottomLeftRadius='0px';
 titleBar.style.cssFloat='left';
 titleBar.innerHTML = '<div align="center"><img src="icons/map_title.png" width="230" height="252" border="0"/></div>';
 controlUI.appendChild(titleBar);

 yellowStripe = document.createElement('div');
 yellowStripe.style.backgroundColor = '#FFFF00';
 yellowStripe.style.height='2px';
 yellowStripe.style.width='245px';
 yellowStripe.style.marginTop='3px';
 yellowStripe.style.marginBottom='3px';
 yellowStripe.style.marginLeft='0px';
 yellowStripe.style.marginRight='0px';
 yellowStripe.style.paddingTop='0px';
 yellowStripe.style.paddingBottom='0px';
 yellowStripe.style.paddingLeft='0px';
 yellowStripe.style.paddingRight='0px';
 yellowStripe.style.cssFloat='left';
 yellowStripe.style.fontFamily='Georgia, serif';
 yellowStripe.style.fontSize='14px';
 controlUI.appendChild(yellowStripe);

 //'Smaller' events button.
 smallEvents = document.createElement('div');
 smallEvents.style.height='108px';
 smallEvents.style.width='129px';
 smallEvents.style.marginTop='0px';
 smallEvents.style.marginBottom='0px';
 smallEvents.style.marginLeft='0px';
 smallEvents.style.marginRight='0px';
 smallEvents.style.paddingTop='0px';
 smallEvents.style.paddingBottom='2px';
 smallEvents.style.paddingLeft='0px';
 smallEvents.style.paddingRight='0px';
 smallEvents.style.cssFloat='left';
 smallEvents.innerHTML = '<div align="center" onClick="handelRequests(\'small_events\')" OnMouseOver="this.style.cursor=\'pointer\';" OnMouseOut="this.style.cursor=\'default\';"><img src="icons/button_small_event.png" width="128" height="107" border="0"/></div>';
 controlUI.appendChild(smallEvents);

 //Umbrella button
 brolly = document.createElement('div');
 brolly.style.height='149px';
 brolly.style.width='94px';
 brolly.style.marginTop='0px';
 brolly.style.marginBottom='0px';
 brolly.style.marginLeft='0px';
 brolly.style.marginRight='0px';
 brolly.style.paddingTop='0px';
 brolly.style.paddingBottom='2px';
 brolly.style.paddingLeft='0px';
 brolly.style.paddingRight='0px';
 brolly.style.cssFloat='left';
 brolly.innerHTML = '<div align="center" onClick="handelRequests(\'rainfall\')" OnMouseOver="this.style.cursor=\'pointer\';" OnMouseOut="this.style.cursor=\'default\';"><img src="icons/button_brolly.png" width="93" height="148" border="0"/></div>';
 controlUI.appendChild(brolly);

}

Quick note about styles: The code above uses the style property of an element to define its styles using JavaScript. To convert CSS properties to their JavaScript notation, you just need to remember that properties which do not have a hyphen stay the same, while those with a hyphen are converted to camelCase, e.g. background-image becomes backgroundImage. The exception is float, which becomes cssFloat.

Step 2

Then, create a div to hold the menu, and add the menu to this div by calling the function you’ve just created in step 1.

//Create control panel (ie. site title and 2 buttons) which appears on the right-hand side.
var controlPanelDiv = document.createElement('div');
var festivalMapControlPanel = new createControlPanel(controlPanelDiv, festivalMap);

Step 3

Then set the controls property of your map to add the menu to the relevant location, in this case RIGHT_TOP.

//Add the control panel and reset button (created previously) to the map.
festivalMap.controls[google.maps.ControlPosition.RIGHT_TOP].push(controlPanelDiv);

Step 4

By now you should have something that looks like a custom menu showing on the right hand side of your map. So all that’s left to be done is to get your menu to do something...

Over the course of the next tutorials we'll get the Smaller Events button to reveal photos from Flickr, and the umbrella button to show a sketched rainfall map overlay. (A cliché I know, but we do get a fair bit of rain here during the summer months!)

So to get something functioning in this tutorial, I’ve repeated the above steps to also add a Reset button to the RIGHT_BOTTOM area. This includes wrapping code around the Reset button to call the handelRequests function.

//Function that creates the 'Reser map' button.
function createResetButton (resetButtonDiv){
 resetButtonDiv.style.padding = '0px';
 controlUI2 = document.createElement('div');
 controlUI2.style.backgroundColor = '#ffffff';
 controlUI2.style.borderRadius='5px';
 controlUI2.style.margin='10px';
 controlUI2.style.paddingTop='2px';
 controlUI2.style.paddingBottom='2px';
 controlUI2.style.paddingLeft='2px';
 controlUI2.style.paddingRight='5px';
 controlUI2.style.textAlign='center';
 controlUI2.style.width='148px';
 controlUI2.style.height='31px';
 controlUI2.innerHTML = '<div onClick="handelRequests(\'reset\')" OnMouseOver="this.style.cursor=\'pointer\';" OnMouseOut="this.style.cursor=\'default\';" ><img src="icons/button_reset.png" width="148" height="31" border="0"/></div>';
 resetButtonDiv.appendChild(controlUI2);
}

Step 5

The  handelRequests function does a few things - take a look at the comments in the code.


//Function that is called when either the 'smaller events', unbrella or the 'reset map' buttons are clicked.
function handelRequests (buttonPressed) {
if (buttonPressed === "reset"){
 //Resets the zoom, map position and marker z-indexes back to their orignal position. Also closes all infoboxes currently open.
 festivalMap.setZoom(festivalMapZoom);
 festivalMap.setCenter(festivalMapCenter);
 resetZindexes();
 //This is a function I've created that closes any info boxes that are open.
 closeAllInfoboxes();
}
else if (buttonPressed === "small_events"){
 alert("This button will do something useful in a later tutorial!");
}
else if (buttonPressed === "rainfall"){
 alert("This button will do something useful in a later tutorial!");
}
}

You can get a full copy of the html file for this part of the tutorial from the top of this page, or take a look at the live example. Only the Glastonbury map marker is 'active in this example.


What Next?

That's it for this tutorial! Hopefully you now have something working which vaguely resembles the picture at the top of this page. If something has gone amiss, take a look through the downloadable files available on GitHub. I've included the code in separate files for each part of this tutorial, so you should be able to work out where it all went wrong.

The next tutorial will look at how you can use the Google maps API to bring your own maps (e.g. a handrawn or historic map) to life, making them zoomable and interactive.


Image Credits

The photographs used within this tutorial have all been released under the Creative Commons licence.

Advertisement