When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, May 26, 2010

Building a Store Locator ASP.NET Application Using Google Maps API (Part 2)

By Scott Mitchell


Introduction


Last week's article, Building a Store Locator ASP.NET Application Using Google Maps API (Part 1), was the first in a multi-part article series exploring how to add store locator-type functionality to your ASP.NET website using the free Google Maps API. Part 1 started with an examination of the database used to power the store locator, which contains a single table named Stores with columns capturing the store number, its address and its latitude and longitude coordinates. Next, we looked at using Google Maps API's geocoding service to translate a user-entered address, such as San Diego, CA or 92101 into its latitude and longitude coordinates. Knowing the coordinates of the address entered by the user, we then looked at writing a SQL query to return those stores within (roughly) 15 miles of the user-entered address. These nearby stores were then displayed in a grid, listing the store number, the distance from the address entered to each store, and the store's address.

While a list of nearby stores and their distances certainly qualifies as a store locator, most store locators also include a map showing the area searched, with markers denoting the store locations. This article looks at how to use the Google Maps API, a sprinkle of JavaScript, and a pinch of server-side code to add such functionality to our store locator. Read on to learn more!

- continued -

Displaying a Map in a Web Page Using Google Maps API


The Google Maps API is a free and easy to use API for displaying interactive maps on a web page using a bit of client-side JavaScript. To use the Google Maps API from a web page you first need to reference the API via a <script> element like so:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

This <script> element needs to appear on every page that uses the Google Maps API. You can add it on a page-by-page basis or in the master page. Once the API has been referenced, you can use it in your client-side JavaScript. The API offers a host of classes, each with a slew of properties, methods, and events. The Google Maps JavaScript API Reference lists these classes and their members and is an invaluable resource when getting started with Google Maps API.

To display a map in a web page you need to create the map canvas, which is the area on the web page where the map will be "painted." The map canvas is simply an HTML element in the page, and is typically implemented by adding a <div> element to the page's markup like so:

<div id="my_map" style="width:500px;height:400px"></div>

The id attribute identifies the <div> element and will be used to reference the element by the JavaScript that displays the map. The style attribute specifies the dimensions of the map - in this case the map will be 500 pixels wide by 400 pixels high. You could also use relative measurements (like a width of 100%) and include additional style settings, like a border.

With the Google Maps API referenced via a <script> element and with the map canvas in place, all that remains is to write the JavaScript to display the map on the map canvas. To display a map we use the Map class and need to pass in three bits of information:

  • The latitude and longitude coordinates of where to center the map,
  • The zoom level, and
  • The map type
The latitude and longitude coordinates are specified via a LatLng object. The zoom is an integer value that ranges from zero on up. A zoom level of zero shows the entire world. All points on the map can zoom up to 19, while certain parts of the globe can have zoom levels of 20 and beyond. The map type indicates whether the map shows just roads, shows satellite data, shows a hybrid of road and satellite data, or shows terrain.

The following markup and JavaScript displays a hybrid map of Mexico City, Mexico.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
   <title>Mexico City, Mexico</title>

   <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
   
   <script type="text/javascript">
      function init_map(map_canvas_id, lat, lng, zoomLevel) {
         var myLatLng = new google.maps.LatLng(lat, lng);

         var options = {
            zoom: zoomLevel,
            center: myLatLng,
            mapTypeId: google.maps.MapTypeId.HYBRID
         };

         var map_canvas = document.getElementById(map_canvas_id);

         var map = new google.maps.Map(map_canvas, options);
      }

   </script>
</head>
<body>
   <h2>Mexico City, Mexico</h2>
   
   <div id="my_map" style="width:400px;height:300px"></div>
   
   <p>
      You are viewing a hybrid map of Mexico City!
   </p>
   
   <script type="text/javascript">
      init_map('my_map', 19.4270499, -99.1275711, 14);
   </script>
</body>
</html>

First, note the map canvas (my_map), which defines a canvas of 400x300 pixels. There's also some other HTML markup on the page - an <h2> heading and some text that explains what the visitor is viewing.

In the <head> section there are two <script> tags. The first one pulls in the Google Maps API while the second one defines a function named init_map. The init_map function accepts four input parameters:

  • map_canvas_id - the id value of the map canvas element (my_map, in this instance),
  • lat - the latitude coordinate of where the map should be centered,
  • lng - the longitude coordinate of where the map should be centered,
  • zoomLevel - the zoom level
The init_map function starts by creating a LatLng object modeling the lat and lng values passed into the function. Next, an options object is defined as having three properties - zoom, center, and mapTypeId - and these properties are set to the passed-in zoom level (zoomLevel), the LatLng object based on the passed-in lat and lng values (myLatLng), and the MapTypeId value HYBRID, which indicates that the a hybrid map should be displayed (one that shows both road and satellite information).

Next, the map canvas is retrieved from the HTML DOM via the getElementById function. Finally, a new Map object is created by passing in the map canvas and the map's options. That's all there is to it! When viewing the page through a browser you will see a 400x300 pixel hybrid map centered in the heart of Mexico City, Mexico. Like with any other Google map, the visitor can zoom in and zoom out and scroll the map by clicking and dragging her mouse on the map surface.

The final piece of the puzzle is the <script> block at the end of the HTML document. Here we call the init_map function passing in the id of the map canvas (my_map), the latitude and longitude of the map center (19.4270499 and -99.1275711), and the zoom level (14).

The screen shot below shows this sample page when initially viewed through a browser.

A hybrid map centered on Mexico City, Mexico is displayed.

Adding Markers to the Map


At this point we have seen how to use the Google Maps API to display a map centered at a specific latitude and longitude location. While this is certainly useful in many scenarios, a map on its own is not as useful when building a store locator as it does not show where the nearby stores are located. The good news is that with a bit more JavaScript we can add markers to the map. The Google Maps API includes a Marker class that can be placed at a specific latitude and longitude. To add a marker to a map first create the Map object (like we did in the sample above) and then use the following pattern:

var marker = new google.maps.Marker({ title: "Marker Title", position: new google.maps.LatLng(lat, lng) });
marker.setMap(mapObject);

Marker Title is the title of the marker and appears as a tooltip when the user hovers over the marker; lat and lng indicate the latitude and longitude coordinates of where the marker should be placed.

To demonstrate using markers, let's create another Mexico City map that includes markers showing museums of interest. I found a bunch of Mexico City museums listed online at http://www.mexicocity.com.mx/museum.html, which included the name of the museum and its address. I then used the Google geocoding service discussed last week to translate those addresses to latitude and longitude coordinates. I then combined this into an array of title and position values using the following JavaScript:

var markers =
   [
      {
         title: "National Art Museum",
         position: new google.maps.LatLng(19.4351262, -99.1334024)
      },
      {
         title: "Viceroyal Painting Gallery",
         position: new google.maps.LatLng(19.4331008, -99.1492114)
      },
      {
         title: "Carmelitan Museum",
         position: new google.maps.LatLng(19.3028860, -99.2352628)
      },
      {
         title: "San Carlos Museum",
         position: new google.maps.LatLng(19.3201515, -99.2265153)
      }
   ];

Next, I added the following code to the init_map function, which we created in the previous example. (I also changed the map's MapTypeId value from HYBRID to ROADMAP.)

function init_map(map_canvas_id, lat, lng, zoomLevel) {
   ...

   var map = new google.maps.Map(map_canvas, options);

   // Place markers
   for (var i = 0; i < markers.length; i++) {
      var marker = new google.maps.Marker(markers[i]);
      marker.setMap(map);
   }

}

The above code adds the four markers defined in the markers array to the map. However, if you view the page through a browser you'll see that not all markers are initially displayed. The map is centered in the heart of Mexico City at a zoom level that only captures two of the four markers (see the screen shot below). If you zoom out you'll soon see the other two markers, which are in the southwest part of the city.

The map initially shows only two of the four markers. The other two markers can be seen by zooming out.

The Google Maps API includes a LatLngBounds object that can be used to define a boundary that includes all of the markers added to the map; moreover, this boundary can be used to center and zoom the map so that the initial map includes all of the markers. To use this object, we need to update the init_map function to create a LatLngBounds object, to extend it with each new marker, and then to set the map's center and zoom level based on the bounds. The following code is all that is needed to achieve this:

function init_map(map_canvas_id, lat, lng, zoomLevel) {
   ...

   var map = new google.maps.Map(map_canvas, options);

   var bounds = new google.maps.LatLngBounds();

   // Place markers
   for (var i = 0; i < markers.length; i++) {
      var marker = new google.maps.Marker(markers[i]);
      marker.setMap(map);

      bounds.extend(marker.getPosition());
   }

   // Center/zoom the map based on the bounds
   map.fitBounds(bounds);
   map.setCenter(bounds.getCenter());

}

With the above code in place, the initial map view is appropriately centered and zoomed, as the following screen shot illustrates.

The map is appropriately centered and zoomed to show all four markers.

Tying It All Together - Displaying a Map and Markers for the Store Locator


Now that we know how to display a map and add markers to it, we're ready to update our store locator results page to show a map based on the address entered by the user and to add markers on the map for any nearby stores. Recall that the search results are displayed in the ShowStoreLocations.aspx page and that this page uses Google's geocoding service to translate the user-entered address into latitude and longitude coordinates. In Part 1 we used these coordinates to determine which store locations were nearby and to compute the distance from the user-entered address to each nearby store location. We'll use these same coordinates as the initial center for our map.

Before we get there, though, let's first take a look at the HTML we need to add to the ShowStoreLocations.aspx page to display the map. There are three important pieces:

  • A <script> tag that references the Google Maps API
  • A <script> tag referencing the script file GoogleHelpers.js. This is a script file in the Scripts folder that contains the init_map function. The init_map function in GoogleHelpers.js is virtually identical to the one we examined in the Adding Markers to the Map section. The only difference is that instead of having a markers array defined external to the function the markers array is passed into the function as an input parameter.
  • A map canvas implemented as a <div> element named map_canvas. The map canvas appears above the grid of nearby store locations.
The heavy lifting happens in the Page_Load event in the code-behind class. Here, the nearby store locations are retrieved and enumerated. For each nearby store location a chunk of JavaScript is created, defining an object with title and position properties. The title property displays the store number of the nearby store whereas the position property is a LatLng object modeling the latitude and longitude values of the store. These objects are combined into an array and then passed into the init_map function.

The following C# code shows the above workflow. The demo available for download at the end of this article include code in both C# and VB, so if you want to see the VB equivalent be sure to download the demo. A couple of comments about the code: first, recall that the nearby store locations are pulled from the database using a SqlDataSource control (dsSearchResults) that is bound to a ListView control on the page. In order to get the list of nearby stores for the purpose of adding the markers I call the SqlDataSource control's Select method, which returns a DataView object. Next, each row in the DataView is enumerated and for each row the appropriate JavaScript object is created.

Because I am using string.Format to craft this JavaScript text any literal curly braces - { and } - need to be escaped by using a pair of them, which is why you see the {{ and }} at the start and end of the object declaration. At the end of the loop JavaScript's object notation is used to define an array of the title/position objects. This string that represents the array, locationsJson, is passed into the init_map function as its fifth input parameter in the RegisterStartupScript method. The RegisterStartupScript method adds the specified JavaScript at the end of the page; for more information on injecting client-side code into an ASP.NET page from server-side code, see Working with Client-Side Script. (If you have trouble grasping the below code I suggest downloading the code and running the demo. When viewing the store locations for a particular search, do a View/Source and examine the JavaScript that was emitted; namely, search for init_map and you'll see the array of markers passed into the function.)

// Loop through each nearby location and build up the JavaScript to place the markers
var markers = new List();

var nearbyLocations = dsSearchResults.Select(DataSourceSelectArguments.Empty) as DataView;
foreach (DataRowView location in nearbyLocations)
{
    markers.Add(string.Format(
                    @"{{
                        title: ""Store #{0}"",
                        position: new google.maps.LatLng({1}, {2})
                    }}",
                     location["StoreNumber"],
                     location["Latitude"],
                     location["Longitude"]
                    )
                 );
}

var locationsJson = "[" + string.Join(",", markers.ToArray()) + "]";

// Inject the Google Maps script
ClientScript.RegisterStartupScript(this.GetType(), "Google Maps Initialization",
                                 string.Format("init_map('map_canvas', {0}, {1}, 13, {2});", lat, lng, locationsJson), true);

With the above code in place we have a much nicer search results page for our store locator. As the following screen shot shows, the search page not only lists the nearby stores but also shows a map with markers for each store, and hovering over a marker displays the store number in a tooltip.

The store locator results page shows the nearby stores in a grid and on a map.

Pretty cool!

Conclusion


While the store locator has been enhanced to include a map with markers for each store location, it still leaves a bit to be desired. For instance, each of the markers on the map looks the same. Hovering your mouse over a marker shows a tooltip, but it would be nice to be able to click the marker and see details about the store, such as its address, its hours of operation, and a phone number. Finally, there's no quick way for a user unfamiliar with the area to know which marker corresponds to which result in the grid below. Ideally, each result in the grid would be numbered and the corresponding marker in the map would be numbered to match. We'll update the map to include these enhancements in Part 3.

  • Read Part 3
  • Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the code used in this article

    Further Readings:


  • Building a Store Locator ASP.NET Application Using Google Maps API (Part 1)
  • Building a Store Locator ASP.NET Application Using Google Maps API (Part 3)
  • The Google Maps API
  • Working with Client-Side Script
  • Implementing the Store Locator Application Using ASP.NET MVC (Part 1)


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article