Showing posts with label Google maps. Show all posts
Showing posts with label Google maps. Show all posts

Thursday, October 09, 2008

Using Google Maps with the MVC Framework

For the last week or so I've been having a lot fun at work adding some Google map functionality to our application. It's an internal application for my clients, so I can't show you any of the code, but I've put together a little demo to demonstrate some of the techniques. The core message here is that it's easy. Taking a some geographic information from your model and putting some markers on a google map with a bit of interactive functionality is really only a matter of a few lines of code. You can download a zip file of the solution here:

http://static.mikehadlow.com/Mike.GoogleMaps.zip

Here's the application. It's pretty simple, it just displays a map with a collection of locations. For fun I've made it show some of the places I've been lucky enough to live in during my fun-packed life :) Each location has a latitude, longitude, name and an image. You can click on the marker for a location and its name is displayed in a speech bubble (known as an 'info window' by Google) and it's image is displayed to the right of the map.

googleMapDemo

I build the map object graph in memory with a simple 'repository' (this is just a demo, your repository would normally talk to a database).

using Mike.GoogleMaps.Models;
namespace Mike.GoogleMaps.Repositories
{
    public class MapRepository
    {
        public Map GetById(int id)
        {
            return new Map
                   {
                       Name = "Places Mike has lived",
                       Zoom = 1,
                       LatLng = new LatLng { Latitude = 0.0, Longitude = 0.0, },
                       Locations =
                           {
                               new Location
                               {
                                   Name = "St. Julians, Sevenoaks, UK",
                                   LatLng = new LatLng { Latitude = 51.25136, Longitude = 0.21992 },
                                   Image = "st_julians.jpg"
                               },
                               new Location
                               {
                                   Name = "Kadasali, Gujerat, India",
                                   LatLng = new LatLng { Latitude = 21.235142, Longitude = 71.4462 },
                                   Image = "india.jpg"
                               },
                               // ...
                           }
                   };
        }
    }
}

Next we have a controller action that returns the object graph serialized as JSON:

public ActionResult Map()
{
    var mapRepository = new MapRepository();
    var map = mapRepository.GetById(1);
    return Json(map);
}

On the home controller's index view we have some simple HTML that has div placeholders for the content. One for the map name, another for the map itself and two more for the dynamic location name and image. Please forgive the inline CSS :(

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><title>
 Mike's Google Maps Demo
</title><link href="Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="../../Content/jquery-1.2.6.min.js" type="text/javascript"></script>
    <script src="http://www.google.com/jsapi?key="<key>" type="text/javascript"></script>
    <script src="../../Scripts/LocationsMap.js" type="text/javascript" ></script>
</head>
<body>
    <div class="page">
...
  <h2 id="mapName"></h2>
  <div id="map" style="width : 700px; height : 400px; margin : 0px; padding : 
   0px; float : left; margin-right:20px;"></div>
     
  <p id="info"></p>
  <img id="image" src="" />
  <div style="clear:both;"></div>
...
    </div>
</body>
</html>

Note that this is the HTML as rendered and is a combination of the master view and the home controller's index view. Also note the script references for the Google maps API, jQuery and the LocationMap.js script which controls the page.

jQuery makes writing Javascript a dream. I am a Javascript novice, but I found it blissfully easy to write this code. Here's the javascript which does all the work:

google.load("maps", "2");
// make a json request to get the map data from the Map action
$(function() {
    if (google.maps.BrowserIsCompatible()) {
        $.getJSON("/Home/Map", initialise);
    }
});
function initialise(mapData) {
    $("#mapName").text(mapData.Name);
    // create the map
    var map = new google.maps.Map2($("#map")[0]);
    map.addControl(new google.maps.SmallMapControl());
    map.addControl(new google.maps.MapTypeControl());
    map.setMapType(G_SATELLITE_MAP);
    var latlng = new google.maps.LatLng(mapData.LatLng.Latitude, mapData.LatLng.Longitude);
    var zoom = mapData.Zoom;
    map.setCenter(latlng, zoom);
    // set the marker for each location
    $.each(mapData.Locations, function(i, location) {
        setupLocationMarker(map, location);
    });
}
function setupLocationMarker(map, location) {
    // create a marker
    var latlng = new google.maps.LatLng(location.LatLng.Latitude, location.LatLng.Longitude);
    var marker = new google.maps.Marker(latlng);
    map.addOverlay(marker);
    // add a marker click event
    google.maps.Event.addListener(marker, "click", function(latlng) {
        
        // show the name and image on the page
        $("#info").text(location.Name);
        $("#image")[0].src = "../../Content/" + location.Image;
        
        // open the info window with the location name
        map.openInfoWindow(latlng, $("<p></p>").text(location.Name)[0]);
    });    
    
}

When the page loads we make an ajax request 'getJSON' to the HomeController's Map action listed above. When the call completes, it fires the callback function 'initialise'. This creates the map and binds it to the map div. We set the centre of the map to the map object's LatLng and the zoom level to the map's Zoom value.

Next we iterate (using jQuery's $.each()) through the locations and call setupLocationMarker for each one. This creates a new Marker object for each location and adds it to the map. It also adds a click event handler to each marker to set the name and image url, and popup the info window.

Simple and easy. I've been really impressed by the power of jQuery. It's very good news that Microsoft have adopted it. With the Firebug plugin for Firefox doing javascript development is a real pleasure.  As for the Google maps API, it is nicely conceived and has excellent documentation.

So what's stopping you? Get mapping!