Yango Blog

C#, PHP and MySQL fun

Yango Blog header image 2

AJAX Google Maps Clone

January 19th, 2008 · 11 Comments

Introduction

The Google Maps AJAX interface is a very clever piece of coding, It allows the user to navigate around a two dimensional map using input from the mouse. The Google Maps interface can be seen at http://maps.google.com. What I will outline here is how to create an area on a web page which you can pan around with images in the background.

A link to a simple example is here My Google Maps Clone

HTML Code

There is very little HTML code to go with the example.

<br clear="all" />
<div id="quilt_dragLayer"></div>
<div id="quilt">

Here we can see that the HTML simply contains two div tags. The first is the special one, the second contains all the content. The CSS for the two div’s allows the first one to be transparent, and the second one the have an overflow of hidden.

#quilt {
width: 640px;
height: 480px;
background-position:center;
background-repeat:no-repeat;
background-color:#000000;
position:relative;
overflow:hidden;
z-index:0;
}

#quilt_dragLayer {
width: 640px;
height: 480px;
position:absolute;
background-image:url(../images/transparent-pixel.gif);
background-color: transparent;
border:#999999 1px solid;
z-index:2;
cursor: -moz-grab, url("images/grab.cur"), default;
}

So we can see from the CSS the quilt_dragLayer is transparent. The reason for the transparent layer on top of the display layer is to stop the mouse dragging from highlighting the objects displayed and turning them a nasty blue shade.

Javascript Code

The javascript code is the key to the Google Maps Clone working, the script has to handle the mouse events and create images to be displayed in the display area. For this example the MooTools framework was used. See http://www.mootools.net for more information about the framework.

The code implements handlers for three mouse events, move, down and up, in addition to these the over and out events are implemented to change the cursor.

The Mouse Down handler

The mouse down handler enables the dragging of the quilt, the code is shown below;

function QuiltMouseDown(event)
{
drag = true;
dragStartXPos = event.client.x;
dragStartYPos = event.client.y;
setCursor(true);
};

The function sets drag to true, enabling dragging, sets up the start position of the drag and makes the cursor ‘grab’ the area.

The mouse move handler moves the area when drag is set to true. The move function then calculates the region containing the visable blocks before calling the addMissingBlocks function to add in any missing blocks.

function QuiltMouseMove(event)
{
if (drag)
{
var blockStartX = 99999;
var blockStartY = 99999;
var blockEndX = 0;
var blockEndY = 0;
var coordStartX = 99999;
var coordStartY = 99999;
var coordEndX = 0;
var coordEndY = 0;
var numBlockEndX = 0;
var numBlockEndY = 0;
$$('img.imageBox').each(function(p){
///


/// block to move all elements in thwe quilt to the new mouse position
///

var y = event.client.y - dragStartYPos;
var x = event.client.x - dragStartXPos;
y += parseInt(p.getProperty(’y'));
x += parseInt(p.getProperty(’x'));
p.setStyles({
‘top’: y+’px’,
‘left’: x+’px’
});
p.setProperties({
‘x’:x,
‘y’:y
});
///
/// finally check if the block is visable in its new location
/// if not remove, if it is check it and build the region which the block ocupies
///

if (!IsVisible(p))
{
$(’quilt’).removeChild(p);
}
else
{
///
/// recompute the region
///

if (x < blockStartX)
{
blockStartX = x;
}
if (y < blockStartY)
{
blockStartY = y;
}
if ((x + imgWidth) > blockEndX)
{
blockEndX = (x + imgWidth);
numBlockEndX = 1;
}
else if ((x + imgWidth) == blockEndX)
{
numBlockEndX += 1;
}
if ((y + imgHeight) > blockEndY)
{
blockEndY = (y + imgHeight);
numBlockEndY = 1;
}
else if ((y + imgHeight) == blockEndY)
{
numBlockEndY += 1;
}
var cy = parseInt(p.getProperty(’cy’));
var cx = parseInt(p.getProperty(’cx’));
///
/// recompute the region
///

if (cx < coordStartX)
{
coordStartX = cx;
}
if (cy < coordStartY)
{
coordStartY = cy;
}
if ((cx + imgWidth) > coordEndX)
{
coordEndX = (cx + imgWidth);
}
if ((cy + imgHeight) > coordEndY)
{
coordEndY = (cy + imgHeight);
}
}
});
if (numBlockEndX < 2)
{
blockEndX -= imgWidth;
coordEndX -= imgWidth;
}
if (numBlockEndY < 2)
{
blockEndY -= imgHeight;
coordEndY -= imgHeight;
}
///
/// add in img’s to fill the empty space made by the drag
///

addMissingBlocks(blockStartX, blockStartY, blockEndX, blockEndY, coordStartX, coordStartY, coordEndX, coordEndY);
dragStartXPos = event.client.x;
dragStartYPos = event.client.y;
}
};

The addMissingBlocks function adds the missing blocks of the area’s background.

function addMissingBlocks(blockStartX, blockStartY, blockEndX, blockEndY, coordStartX, coordStartY, coordEndX, coordEndY)
{
var q = $('quilt').getCoordinates();
///


/// check to see if the top has space around it
///

if (blockStartY > (q.top - 50))
{
var numRows = blockStartY / imgHeight;
var numCols = (q.width / imgWidth) + 1;
var xStart = blockStartX;
var cxStart = coordStartX;
while (xStart > 0)
{
xStart -= imgWidth;
cxStart -= imgWidth;
}
var yStart = blockStartY;
var cyStart = coordStartY;
while (yStart > 0)
{
yStart -= imgHeight;
cyStart -= imgHeight;
}
for (var r = 0; r < numRows; r++)
{
var cX = xStart;
var ccX = cxStart;
for (var c = 0; c < numCols; c++)
{
addImg(cX, yStart, ccX, cyStart);
cX += imgWidth;
ccX += imgWidth;
}
yStart += imgHeight;
cyStart += imgHeight;
}
}
///
/// check to see if the left has space around it
///

if (blockStartX > (q.left - 50))
{
var numRows = (blockEndY - blockStartY) / imgHeight;
var numCols = blockStartX / imgWidth;
var xStart = blockStartX;
var cxStart = coordStartX;
while (xStart > 0)
{
xStart -= imgWidth;
cxStart -= imgWidth;
}
var yStart = blockStartY;
var cyStart = coordStartY;
for (var r = 0; r < numRows; r++)
{
var cX = xStart;
var ccX = cxStart;
for (var c = 0; c < numCols; c++)
{
addImg(cX, yStart, ccX, cyStart);
cX += imgWidth;
ccX += imgWidth;
}
yStart += imgHeight;
cyStart += imgHeight;
}
}
///
/// check to see if the bottom has space around it
///

if (blockEndY < (q.bottom + 50))
{
var numRows = ((q.bottom + 50) - blockEndY) / imgHeight;
var numCols = (q.width / imgWidth) + 1;
var xStart = blockStartX;
var cxStart = coordStartX;
while (xStart > 0)
{
xStart -= imgWidth;
cxStart -= imgWidth;
}
var yStart = blockEndY;
var cyStart = coordEndY;
for (var r = 0; r < numRows; r++)
{
var cX = xStart;
var ccX = cxStart;
for (var c = 0; c < numCols; c++)
{
addImg(cX, yStart, ccX, cyStart);
cX += imgWidth;
ccX += imgWidth;
}
yStart += imgHeight;
cyStart += imgHeight;
}
}
///
/// check to see if the right has space around it
///

if (blockEndX < (q.right + 50))
{
var numRows = (blockEndY - blockStartY) / imgHeight;
var numCols = ((q.right + 50) - blockEndX) / imgWidth;
var xStart = blockEndX;
var cxStart = coordEndX;
var yStart = blockStartY;
var cyStart = coordStartY;
for (var r = 0; r < numRows; r++)
{
var cX = xStart;
var ccX = cxStart;
for (var c = 0; c < numCols; c++)
{
addImg(cX, yStart, ccX, cyStart);
cX += imgWidth;
ccX += imgWidth;
}
yStart += imgHeight;
cyStart += imgHeight;
}
}
}

So there it is, I hope that this has helped anyone needing to create a Google Maps Clone.

Update: I have updated the code to work with the newer v1.2 of mootools. Just follow the link at the top of the page (or click here). Hope this helps everyone.

Tags: AJAX

11 responses so far ↓

  • 1 Vanessa // Feb 9, 2008 at 6:34 pm

    Nice Site! Thanks!

  • 2 michael // May 27, 2008 at 9:59 pm

    This was exactly what I was looking for. I’m building a large image browser from a new archival API and it may just work out in this format.

    If I make any progress I’ll provide a link back and due credit.

  • 3 Lucas Costa // Jun 16, 2008 at 1:27 pm

    Hey, great app! I started playing with it a little bit. Working very nicely.

    However it doesn’t work with the new version of mootools. I think you changed the core a little bit. Could you comment on that?

    Thanks and congratulations

  • 4 Gary Ruddock // Aug 27, 2008 at 2:46 pm

    Do you have a version that would work with the new mootools or could you give me any pointers on how to change it myself?

  • 5 michael // Oct 9, 2008 at 8:56 am

    Me again - I have a questionand I’ve only just started looking into this myself…

    If you wanted html instead of images, are there any simple ways of allowing links to be clickable etc. ?

    Right now the quilt_dragLayer div blocks clicks etc to the layer below

  • 6 michael // Oct 9, 2008 at 9:40 am

    Figured it out - very simple:

    1. Remove the ‘quilt_dragLayer’ div

    2. Attach events to ‘quilt’ instead of ‘quit_dragLayer’

    3. Instead of calling ‘addImg’, replace with calls to ‘addDiv’ instead:

    function addDiv(x, y, cx, cy)
    {
    var div = new Element(’div’);
    $(’quilt’).appendChild(div);
    div.setStyles({
    ‘top’: y+’px’,
    ‘left’: x+’px’
    });

    div.addClass(’imageBox’);

    div.setStyles({
    ‘width’: imgWidth+’px’,
    ‘height’: imgHeight+’px’
    });

    div.innerHTML = “Div x=” + x + ” y=” + y + “, cx=” + cx + ” cy=” + cy;

    div.setProperties({
    ‘x’ : x,
    ‘y’ : y,
    ‘cx’: cx,
    ‘cy’: cy
    });
    }

  • 7 SheepiBoy // Oct 22, 2008 at 4:13 am

    Can you explain, how can i add an citymap image wich is 3000 x 3000 px height? And how to make a position on a map file, ex. stree name with search function? :)) Thanx

  • 8 admin // Oct 22, 2008 at 4:21 am

    Hello SheepiBoy,

    You will need to break up the image into smaller chunks, and edit the php server side script to serve them.

    You will then have to work out the location of each street in your map and then when a user searches move them to that location.

    Hope this helps

    Rich

  • 9 Joseph // Jan 29, 2009 at 11:38 am

    I’m interested in creating clickable objects on top of the dragable interface, what would this take? Ideally, I could have small images that I include on top of the “map”, that when clicked run some javascript.

  • 10 jason // Jun 1, 2009 at 2:06 am

    Hello everyone.

    I need help. can anybody tell me where to put the image files in the server so that it could load the image to the image block. or is there any way to get different images loaded in the pc itself (without any server or domain) to test it . currently the script loads the album.php’s image only and repeats it in each block.

    Please help if anybody know
    Thanks in advanced………….

  • 11 AndrewBoldman // Jun 4, 2009 at 1:42 pm

    Hi, good post. I have been woondering about this issue,so thanks for posting. I’ll definitely be coming back to your site.

Leave a Comment