View Full Version : Make your maps explorable using the Google Maps API (work in progress)

Owen Borseth
11-27-2012, 01:35 AM
Hi guys. I'm a ridiculously horrible artists, but I am a pretty decent web developer. So, I'm going to write up a tutorial on how to make your maps explorable with the Google Maps API. You can see a working example on my website (http://owenborseth.com/legends-of-nuvia-the-soul-of-kaesh/the-world-of-nuvia). As I learn more about creating more advanced maps, etc I'll update this thread. Without further ado, let's jump right in and get started.

Your Map

Your map should be pretty big and it needs to have a height and width in pixels that are divisible by 256. I personally created a map that was 3072 x 3072 and it works pretty well. However; the bigger the better. At 100% the map should be clear and of good quality or it will look pretty crappy when zoomed in. If you've ever used Google Maps, and I know you all have, you know that there are zoom levels that you can control. For what I know so far, the map will be a 256 x 256 pixel image at zoom 0. At zoom 1 it will be a 2 x 2 matrix of 256 x 256 pixel images. Basically, for every zoom level the map will consist of 2*(2^zoom) 256 pixel images. I'm working with other tile makers, including a command line one that will automatically cut up an uploaded map, but so far the following one works best for what I've been doing:

Tiles Generator (http://cdn.mikecouturier.com/blog.mikecouturier.com/tilesgenerator/TilesGenerator.exe)

It's a windows app, so sorry if you have a Mac or use Linux. I'll find other options eventually as I play around more. To split your map you run it like so:

TilesGenerator.exe [maxzoom] [filename]

It will go to work creating all of the necessary tiles from zoom levels 0 through whatever zoom level you specified for [maxzoom]. Remember, if your map is 3072 x 3072 like mine and you specify a [maxzoom] of 4 you will end up with a fully zoomed image that is 4096 x 4096 (256*(2^4)). So, a 4096 x 4096 image would actually be perfect for a map with a [maxzoom] of 4. A smaller map will end up stretching the tiles and become distorted. How much it distorts depends on the size difference and the quality of the image of course.

The command line app will create an "output" directory with images named something like tile_0_0-0.png. Basically, tile_[zoom]_[x]-[y].png. You'll need to upload all of those images to a directory on a web server if you're going to use them via the Google Maps API.

The Code

I put up a basic page with only what's required for the map here (http://owenborseth.com/legends-of-nuvia-the-soul-of-kaesh/map). I documented the **** out of it so it should be pretty self explanatory. You could literally copy and paste the source of that page, change the path to your map tiles, modify or remove the marker information, and have everything work out pretty well. Depending on the size of the map you might need to tweak the min and max zoom levels, but that's pretty much it.

Here's is the HTML and javascript code with comments (let me know if you have any questions):

<!-- Load the Google Maps API javascript file -->
<script type="text/javascript" src="http://maps.google.com/maps/api/js?libraries=geometry&sensor=false"></script>
<!-- Load the Google info bubble javascript file -->
<script type="text/javascript">
var script = '<script type="text/javascript" src="http://google-maps-' +
if (document.location.search.indexOf('compiled') !== -1) {
script += '-compiled';
script += '.js"><' + '/script>';
// you'll need this for IE if using bootstrap or some other frameworks
// also, IE sucks ass
#nuvia-map img
max-width: none;
<!-- Container for displaying latitude longitude coordinates when you click on the map. This is handy for when you are needing to get the lat/lng values for markers. -->
<div id="latLng"></div>
<!-- Container for the actual map. -->
<div id="nuvia-map" style="width:512px; height:512px; margin: 0px auto; border:2px solid #000;"></div>
<script type="text/javascript">
/* <![CDATA[ */

/* Array of marker data for creating your markers and marker info bubbles. */
var markers =
"Steelwell\'s Landing":{lat:56.02, lng:-11.16, title:'Steelwell\'s Landing', flat:true, type:'city1', zoom:{3:true,4:true}, image:'/images/nicubunu/sign_post.svg.png',
info:"Small oasis village in the middle of the Great Gozal Desert."},

"Gozal":{lat:69.47, lng:-76.42, title:'Gozal', flat:true, type:'city2', zoom:{3:true,4:true}, image:'/images/nicubunu/sign_crossroad.svg.png',
info:"Rebel stronghold and capital city of Gozal (rebel) controlled lands. Gozal is a sprawling city with five distinct districts each ruled by descendants of the Imperial traitor Karazal."}
/* Array for markers that are loaded onto the map. */
var activeMarkers = [];

/* Global variable that will contain the Google Maps object. */
var map = null

// Google Maps Demo object
var Demo = Demo || {};
// The path to your tile images.
Demo.ImagesBaseUrl = '/images/map/tiles/';

// NuviaMap class
Demo.NuviaMap = function (container)
// Create map
// This sets the default info for your map when it is initially loaded.
map = new google.maps.Map(container,
zoom: 1,
center: new google.maps.LatLng(-2, 2),
mapTypeControl: false,
streetViewControl: false,
zoomControl: true,
style: google.maps.ZoomControlStyle.SMALL

// Set custom tiles
map.mapTypes.set('nuvia', new Demo.ImgMapType('nuvia', '#4E4E4E'));

// Loop through the marker info array and load them onto the map.
for (var key in markers)
var markerData = markers[key];

var marker = new google.maps.Marker(
position:new google.maps.LatLng(markerData.lat, markerData.lng),
infoBubble:new InfoBubble({
maxWidth: 300,
content:(markerData.image ? '<img src="'+markerData.image+'" width="80" align="left">' : '')+'<h3>'+markerData.title+'</h3>'+(markerData.info ? '<p>'+markerData.info+'</p>' : ''),
shadowStyle: 1,
padding: '10px'
// You can use custom icons, default icons, etc. In my case since a city was a marker the circle icon works pretty well.
// I adjust the scale / size of the icon depending on what kind of city it is on my map.
path: google.maps.SymbolPath.CIRCLE,
scale: markerData.type == 'city1' ? 3 : 5

// We need to trap the click event for th emarker so we can pop up an info bubble.
google.maps.event.addListener(marker, 'click', function()
this.infoBubble.open(map, this);


// This is dumb. We only want the markers to display at certain zoom levels so this handled that.
// Google should have a way to specify zoom levels for markers. Maybe they do but I could not find them.
google.maps.event.addListener(map, 'zoom_changed', function()
var currentZoom = map.getZoom();

for(var i = 0; i < activeMarkers.length; i++)
var thisTitle = activeMarkers[i].title;


// This handles the displaying of lat/lng info in the lat/lng info container defined above in the HTML.
google.maps.event.addListener(map, 'click', function(event)
$('#latLng').html("Latitude: "+event.latLng.lat()+" "+", longitude: "+event.latLng.lng());

// ImgMapType class
Demo.ImgMapType = function (theme, backgroundColor)
this.name = this._theme = theme;
this._backgroundColor = backgroundColor;

// Let Google know what size our tiles are and what our min/max zoom levels should be.
Demo.ImgMapType.prototype.tileSize = new google.maps.Size(256, 256);
Demo.ImgMapType.prototype.minZoom = 1;
Demo.ImgMapType.prototype.maxZoom = 4;

// Load the proper tile.
Demo.ImgMapType.prototype.getTile = function (coord, zoom, ownerDocument)
var tilesCount = Math.pow(2, zoom);

if (coord.x >= tilesCount || coord.x < 0 || coord.y >= tilesCount || coord.y < 0)
var div = ownerDocument.createElement('div');
div.style.width = this.tileSize.width + 'px';
div.style.height = this.tileSize.height + 'px';
div.style.backgroundColor = this._backgroundColor;
return div;

var img = ownerDocument.createElement('IMG');
img.width = this.tileSize.width;
img.height = this.tileSize.height;
// This tells what tile image to load based on zoom and coord info.
img.src = Demo.Utils.GetImageUrl('tile_' + zoom + '_' + coord.x + '-' + coord.y + '.png');

return img;

// Just basically returns the image using the path set way above and the name of the actual image file.
Demo.Utils = Demo.Utils || {};
Demo.Utils.GetImageUrl = function (image)
return Demo.ImagesBaseUrl + image;

// Opacity.
Demo.Utils.SetOpacity = function (obj, opacity /* 0 to 100 */ )
obj.style.opacity = opacity / 100;
obj.style.filter = 'alpha(opacity=' + opacity + ')';

// Create ye ol' map.
google.maps.event.addDomListener(window, 'load', function ()
var nuviaMap = new Demo.NuviaMap(document.getElementById('nuvia-map'));

/* ]]> */
<script data-rocketsrc="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/rocketscript"></script>

P.S. Ya'll should really be using Chrome :P

11-27-2012, 01:33 PM
Cool - will follow this and make a CWBP map with the zoom if it works out.

Owen Borseth
11-27-2012, 02:58 PM
Cool - will follow this and make a CWBP map with the zoom if it works out.

Yes! I would be more than happy to help out with that.

11-27-2012, 03:06 PM
I only see a grey space, on your website, with firefox, that is wrong I guess?

11-27-2012, 03:18 PM
Same for me, with Opera.

11-28-2012, 03:02 AM
I can't see anything.!!!

11-28-2012, 10:56 AM
With IE9, I see a mostly off-white map. If I zoom in two mousewheel clicks, I can see two markers. I don't know if it's missing map data, or just a demonstration of how to do markers at varying levels. The JavaScript of the site is interesting in how it handles the markers, though. One of these years I need to get that sort of functionality working for my own projects.

Owen Borseth
11-28-2012, 08:25 PM
Hmm. It's running on a micro instance at Amazon AWS. Sometimes those things get loaded down and don't perform too well. If you refresh it should hopefully load the tiles of the map.

Edit: IE issues and issues with map tiles loading should be resolved. IE was affected by some bootstrap CSS that I needed to override. Other tile loading issues I believe were related to CloudFlare CDN / caching stuff.

11-28-2012, 08:58 PM
This sounds really interesting.

12-06-2012, 04:31 PM
O-hell, this is nice, gonna try to use it for my encyclopedia.

Though is it possible and if so how can I make the map window on website bigger? On 1920px resolution it looks just a little too small.