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, June 2, 2010

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

By Scott Mitchell


Introduction


Over the past two weeks I've showed how to build a store locator application using ASP.NET and the free Google Maps API and Google's geocoding service. Part 1 looked at creating the database to record the store locations. This database contains a table named Stores with columns capturing each store's address and latitude and longitude coordinates. Part 1 also showed how to use Google's geocoding service to translate a user-entered address into latitude and longitude coordinates, which could then be used to retrieve and display those stores within (roughly) a 15 mile area. At the end of Part 1, the results page listed the nearby stores in a grid. In Part 2 we used the Google Maps API to add an interactive map to the search results page, with each nearby store displayed on the map as a marker.

The map added in Part 2 certainly improves the search results page, but the way the nearby stores are displayed on the map leaves a bit to be desired. For starters, each nearby store is displayed on the map using the same marker icon, namely a red pushpin. This makes it difficult to match up the nearby stores listed in the grid with those displayed on the map. Hovering the mouse over a marker on the map displays the store number in a tooltip, but ideally a user could click a marker to see more detailed information about the store, such as its address, phone number, a photo of the storefront, and so forth.

This third and final installment shows how to enhance the map created in Part 2. Specifically, we'll see how to customize the marker icons displayed in the map to make it easier to identify which marker corresponds to which nearby store location. We'll also look at adding rich popup windows to each marker, which includes detailed store information and can be updated further to include pictures and other HTML content. Read on to learn more!

- continued -

Displaying Custom Marker Icons in a Map


As we saw in Part 2, adding a marker to a map can be accomplished using the Marker object, which is part of the Google Maps API. The following JavaScript creates a new marker at the latitude and longitude coordinates specified by the values lat and lng with the title title. Once created, the marker is added to the map using its setMap method.

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

var marker = new google.maps.Marker(markerOptions);

marker.setMap(mapObject);

Note that the Marker object's constructor is passed an object that contains a variety of properties. In the above snippet of code two options are specified: title and position. These are but two of the several options available. (See the MarkerOptions object specification for a complete list.) Another option is the marker's icon setting, which can be customized by specifying the URL of an image to display for the marker. For example, if we had an image file on our web server in the Images folder named StarIcon.png we could have that image displayed in place of the standard pushpin icon by using the following JavaScript:

var markerOptions =
         {
            title: "Marker Title",
            position: new google.maps.LatLng(lat, lng),
            icon: "/Images/StarIcon.png"
         }

...

To make it easier for the user to determine which store listed in the grid corresponds with which store in the map, let's have each marker on the map use a different icon. Specifically, let's have each store location use an icon numbered 1, 2, 3, and so on. We can then show this same icon in the grid. The screen shot below illustrates this concept. Note how the four locations on the map are identified by icons numbered 1 through 4, and the results in the grid are also numbered 1 through 4. This approach makes it easy to identify what stores in the grid correspond to what stores on the map.

The stores are denoted on the map using numbered icons.

Generating the Custom Marker Icons


This simplest way to display such numeric icons would be to create a series of image files named 1.png, 2.png, and so on, up to perhaps 50.png (or whatever the maximum number of store locations are within a 15 mile area of one another). Each image would be a 20x20 pixel image with the appropriate number. Rather than create these 50 images, I decided instead to generate the images dynamically using the GeneratedImage Web control available from the official ASP.NET CodePlex project. The GeneratedImage control simplifies creating and serving dynamically-generated images from an ASP.NET website; this control was discussed in detail in a previous 4Guys article, Dynamically Generating and Caching Images in ASP.NET with the GeneratedImage Control.

The download available at the end of this article includes the Microsoft.Web.GeneratedImage.dll assembly in the Bin folder, which you'll need to use the GeneratedImage control. It also implements the HTTP Handler that is responsible for actually generating the image, NumberToImageHandler.ashx, which is in the Images folder. In a nutshell, visiting the URL Images/NumberToImageHandler.ashx?number=number generates a PNG image that has a navy circle with white text showing the number number. For example, the image shown below is what is generated by NumberToImageHandler.ashx when visiting the URL Images/NumberToImageHandler.ashx?number=5.

The dynamically-generated image for the icon 5.

Examining the code for the NumberToImageHandler.ashx HTTP Handler would take us a bit off course, so I'm going to pass on exploring this handler further. For more information, download the demo available at the end of this article (which includes both C# and VB code) and read the Dynamically Generating and Caching Images in ASP.NET with the GeneratedImage Control article.

A Word On Dynamically-Generating Images...
While the custom marker icons can certainly be generated dynamically as I've done in this demo, for a real-world project I'd strongly suggest creating static images for each of the possible icons (1.png, 2.png, and so on). The benefits are many:
  • Static images will likely look better. The dynamically-generated ones in my demo are not the most attractive ones; a trained graphic artist could certainly create more appealing icons.
  • There is no extra cost or overhead associated with serving static images; however, there is extra processing that must be performed when generating the dynamic image.
  • Finally, static images are likely to be more aggressively cached by the web server, browser and proxy servers than dynamic images.
I decided to use dynamically-generated images for the custom marker icons because I have absolutely zero skills in the graphics art department. For me, it was much easier (and far more interesting) to write code to generate these custom marker icon images on the fly rather than create static image files.

Updating the Results Page to Use Custom Marker Icons


Now that we have a mechanism to generate a custom marker icon for each search result, the final piece of the puzzle is to update the search results page to use these custom marker icons. This involves two steps. First, we must update the ListView control in the search results page to include the appropriate icon next to the address of each result. This is accomplished by adding the following markup to the ListView's ItemTemplate:

<asp:ListView ID="lvSearchResults" runat="server" ...>
   ...

   <ItemTemplate>
      <asp:Image runat="server" ID="imgIcon"
            ImageUrl='<%# string.Format("~/Images/NumberToImageHandler.ashx?number={0}", Container.DisplayIndex + 1) %>' />
   
      ...   
   </ItemTemplate>
</asp:ListView>

The highlighted markup shows the databinding syntax that assigns the Image control's ImageUrl property to the appropriate URL. Specifically, the ImageUrl property is set to ~/Images/NumberToImageHandlerCS.ashx?number=number, where number is the current index of the item being rendered in the ListView plus 1. Understand that each item in the ListView has an associated DisplayIndex that indicates the position of the item in the collection of items. The first ListView item has a DisplayIndex of 0, the second of 1, and so on. The net result is that the first result in the ListView will display the 1 icon, the second the 2 icon, and so forth.

The final step is to update the markers in the map to display the appropriate icon. In Part 2 we added code to the search results page's Page_Load event handler that loops through each nearby store and builds up a block of JavaScript code that defines the marker options for each marker to appear on the map. This chunk of JavaScript is then passed into the client-side init_map function, which displays the map and adds the markers.

To specify the custom marker icons we need to update the server-side code that generates the array of marker options. Specifically, we need to update it to include the icon property. The following code snippet shows this addition. The foreach loop enumerates the search results and for each nearby store the marker options are created. The only difference in the code below from the code we created in Part 2 is the inclusion of the icon property, which uses the URL Images/NumberToImageHandler.ashx?number=number, where number is the index of the result plus 1. (Note: to see the VB version of this code please download the demo.)

var count = 1;

foreach (DataRowView location in nearByLocations)
{
   locations.Add(string.Format(
               @"{{
                  title: ""Store #{0}"",
                  position: new google.maps.LatLng({1}, {2}),
                  icon: ""Images/NumberToImageHandler.ashx?number={3}""
               }}",
                  location["StoreNumber"],
                  location["Latitude"],
                  location["Longitude"],
                  count
               )
             );

   ...
   
   count++;
}

And that's all there is to it! With the additions to the ListView's ItemTemplate and the Page_Load event handler the grid and map now show numbered icons rather than the standard pushpin icon, thereby making it easier for visitors to match store locations listed in the grid with the store location displayed on the map.

Displaying an Info Window When Clicking a Marker in the Map


If the user hovers her mouse over a map marker, the browser displays a tooltip that contains the marker's title (Store #NNN, in this case). It would be nice to display rich, detailed store information, including the store's address and perhaps a picture of the storefront. This is possible through the use of info windows, which are windows that display content in a floating window above the map. If you've used Google Maps in the past you've certainly seen and experienced info windows. They are the white, balloon windows that appear over a marker when you click it.

Displaying an info window when clicking a marker involves two steps:

  1. Creating the info window by instantiating an InfoWindow object, and
  2. Displaying the info window when the marker is clicked.
This is accomplished through a bit of JavaScript. The following snippet of script starts by defining a single info window named info that displays the text "Hello, world" within an h3 element. Next, a client-side click event handler is created for the marker named marker, which displays the info window affixed to the clicked marker.

// Create the info window...
var info = new google.maps.InfoWindow({ content: "<h3>Hello, world!</h3>" });

// Display the info window when the marker is clicked...
google.maps.event.addListener(marker, 'click', function() {
   info.open(map, marker);
});

Recall that in Part 2 we created a JavaScript function named init_map that is responsible for displaying the map and adding the markers to it. To display info windows with each marker we need to beef up this function so that it can be passed an array of info window content (the message to show in each info window). Then, for each marker we add a new InfoWindow object and create a click event handler for the marker to display its associated InfoWindow object.

Here is an abbreviated form of the init_map function highlighting the new functionality for creating an info window for each marker and displaying the appropriate window when the user clicks the marker. The additions made since Part 2 are highlighted.

function init_map(map_canvas_id, lat, lng, zoom, markers, infoWindowContents) {
   ...

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

         if (infoWindowContents && infoWindowContents.length > i)
            createInfoWindow(map, marker, infoWindowContents[i]);

      }
      
      ...
   }
}

function createInfoWindow(map, marker, infoWindowProperties) {
   var info = new google.maps.InfoWindow(infoWindowProperties);

   google.maps.event.addListener(marker, 'click', function() {
      info.open(map, marker);
   });
}

Note that the init_map function accepts a new input parameter, infoWindowContents, which is an array of info window content. This array is created and supplied to the init_map function in the search result page's Page_Load event handler (in the same loop where the marker JavaScript is constructed). Here's that loop showing the construction of the array of info windows' contents; the construction of the markers JavaScript has been removed for brevity.

foreach (DataRowView location in nearbyLocations)
{
   ...

   infoWindowContents.Add(string.Format(
               @"{{
                  content: ""<div class=\""infoWindow\""><b>Store #{0}</b><br />{1}<br />{2}, {3} {4}</div>""
               }}",
                location["StoreNumber"],
                location["Address"],
                location["City"],
                location["Region"],
                location["PostalCode"]
               )
          );
}

Each store's info window displays the store number (in bold) along with the address. To show more information or to modify the style of the text in the info window, simply modify the HTML assigned to the content property. This block of JavaScript is then passed into the init_map function when the page is loaded. (See the call to the ClientScript.RegisterStartupScript method at the end of the Page_Load event handler.)

With the above code in place, users can now click markers to display information about the corresponding store. The screen shot below show the map after the user has clicked the info window for Store #847. Pretty neat!

An info window displays the store's number and address.

Conclusion


This article and the previous two have demonstrated how to create a simple, free ASP.NET store locator application using the Google Maps API and the Google geocoding service. It's amazing what's possible with just a touch of server-side code and a dash of JavaScript.

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 2)
  • The Google Maps API
  • Dynamically Generating and Caching Images in ASP.NET with the GeneratedImage Control
  • 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