Click here to Skip to main content
15,879,535 members
Articles / Web Development / HTML
Article

Lat Lays Flat - Part 2: Advanced Google Maps .NET Control Usage

Rate me:
Please Sign up or sign in to vote.
4.74/5 (46 votes)
17 Oct 200511 min read 462.6K   7K   157   109
Advanced usage of an ASP.NET server control wrapper for the Google Maps API.

Introduction

This is the second article in a three article series examining a custom ASP.NET server control I developed to make using the Google Maps API easier for .NET developers. This article assumes you have read Part 1 and are familiar with the Google Maps API. You may see references throughout the article to "my control, my GoogleMaps control, my GMap control, etc". I did not create the Google Maps API, I merely created a wrapper for ASP.NET using C#, XML, and XSL.

The main goal of this article is to show you just how powerful a .NET wrapper can be for the Google Maps API. Everything I will cover in this article could be done with other technologies like JavaScript, PHP, Java, etc. But aren't you glad you don't have to use any of those? This also uses an object oriented approach to Google Maps interaction.

The advanced features covered in this article include:

  • Overlay data binding – Bind your database lat/lng values directly to a GMap as overlays.
  • Client call backs – Run the server side code as a result of client side GMap events.
  • GMap postback state – Get some data about your GMap after a postback/callback.

Setup and configuration

Maps are pretty boring without some accompanying GIS data. The three advanced examples I will show you make use of approximately 5,000 data points. Included in the code download is the SQL script required to create the MS SQL Server database and tables used in the examples. Any database can be used, Access, MySQL, FireBird etc. The database included in the download is not an example of good DB design, just something to use for the article purposes. Data used in these examples is freely available (in raw form) from many Internet sites. If you'd like a copy of my massaged data, drop a line on the message board at the bottom of the article (and give me some feedback in the form of a rating).

The good stuff

Included in the downloadable code (see Download source files above) is a web project called TestWeb with three (3) advanced examples on how to use my GMap control in your applications. If you have downloaded the code from Part 1, you can unzip the code for Part 2 right on top of it. In the course of creating the advanced examples, some changes were made to the GMap control and the associated XSL and JavaScript files. None of these changes will break any of the basic examples from Part 1. If you have been experimenting with the control on your own, you can see the documented changes in the changes.txt file. You can find all the three examples here.

ColoradoCounties.aspx

__Show Me__

Image 1

This example makes use of the client callback and overlay data binding advanced features of the GMap control. If you're not familiar with client callbacks, check out my article on Asynchronous JavaScript and XML (AJAX) framework. For a refresher on data binding check out Mastering ASP.NET Data Binding.

The code snippet below creates a GMap control and turns on client callbacks by setting EnableClientCallBacks to True. The code also assigns the event handler gMap_MarkerClick() to the MarkerClick event of the GMap.

ASP.NET
<wcp:GMap runat="server" id="gMap" Width="750px" Height="525px" 
  EnableClientCallBacks="True" OnMarkerClick="gMap_MarkerClick"/>

Now, anytime a map marker is clicked, the server-side code found in gMap_MarkerClick() will be executed. Sweet! The table below shows the complete list of server-side events that can be implemented as client callbacks:

Event nameEvent arguments
GMap.ClickSender – The GMap clicked
GPointEventArgs – The GPoint where the GMap was clicked.
GMap.MarkerClickSender – A GMarker instance representing the marker clicked.
GPointEventArgs – The GPoint where the marker was clicked.
GMap.MoveStartSender – The GMap that was moved.
GPointEventArgs – The GPoint representing the center lat/lng coordinate of the GMap at the start of the move.
GMap.MoveEndSender – The GMap that was moved.
GPointEventArgs – The GPoint representing the center lat/lng coordinate of the GMap at the end of the move.
GMap.ZoomSender – The GMap that was zoomed.
GMapZoomEventArgs – The old and new zoom values.

Also included on the page are some client side events. The code below will open an info window on the map whenever a marker is clicked. The info window will contain the ID of the marker and the word "County". In ColoradoCounties.aspx.cs, we'll examine the code behind that assigns the ID to the marker.

JavaScript
function GMarker_Click()
{
  this.openInfoWindowHtml(this.id + " County");
}

The code below will execute whenever the info window is closed. The code searches the map for a polyline of the same name as the marker. If the polyline is found, it is removed from the map. This is the code that will clear the county boundary from the map:

JavaScript
function GMarker_InfoWindowClose()
{
  var pLineId = this.id + "Boundary";
  var pLine = this.map.getOverlayById(pLineId);
  if( pLine )
    this.map.removeOverlay(pLine);
}

ColoradoCounties.aspx.cs

This is where the magic happens. When the page loads, the code first centers the map over Colorado with an appropriate zoom level. Then the code queries the database to get the Name, Latitude, and Longitude of each county in Colorado (see Setup and configuration above for more information on the database). The data returned is in the form of a DataSet which is then assigned to the DataSource of GMap. Finally, we tell the GMap which database columns correspond to the MarkerId, Longitude, and Latitude for each county. When the GMap is rendered, it will add a marker using the database values for location and ID.

C#
gMap.CenterAndZoom(new GPoint(-105.5F, 39F), 10);
      
DataSet ds = GetCounties();

gMap.DataSource = ds;
gMap.DataMarkerIdField  = "CountyName";
gMap.DataLongitudeField = "Longitude";
gMap.DataLatitudeField  = "Latitude";
gMap.DataBind();

gMap.AddControl(new GSmallMapControl());

Remember when we assigned gMap_MarkerClick() to the MarkerClick event in the ASPX page? Now we'll examine the code associated with that event handler. The goal here is to read the lat/lng values from the database that represent the boundary of each county. We will take those values and translate them into a GPolyline that will show the boundary in red on the GMap. For performance reasons, we use caching to speed up client call backs.

C#
protected string gMap_MarkerClick(object s, GPointEventArgs pea)
{
  GMarker gm = s as GMarker;
  if( gm != null )
  {
    string gpTransform = Cache[gm.Id] as string;
    if( gpTransform == null )
    {

If the boundary isn't already cached we query the database for the lat/lng values. Because in our page load we used data binding to assign the county name to the marker ID, we use that same value (gm.Id) to query the database for the boundary values.

C#
GPolyline gp = GetBoundaryByCounty( gm.Id );
gp.Color = _BoundaryColor;
gp.Weight = _BoundaryWeight;
gp.Opacity = _BoundaryOpacity;

Finally, we use the XSL style sheet used to transform the GMap control into JavaScript to turn our new GPolyline boundary into JavaScript. That JavaScript is returned to the client where it is then executed using the JavaScript eval() function. Viola! We have a county boundary. __Show Me__.

C#
      string path = Server.MapPath("../Scripts/GMap.xsl");
      gpTransform = GXslt.Transform(gp, path, gMap.GetXsltArguments());
      Cache[gm.Id] = gpTransform;
    }
    return gpTransform;
  }
  return String.Empty;
}

StateQuarters.aspx

__Show Me__

Image 2

This example makes use of the client callback and overlay data binding advanced features of the GMap control. I re-engineered the US Mint 50 States Quarter Program using the GMap control. When you click the marker representing a state, you will see some state information along with a graphic of the state's quarter. The page itself is just some style sheet information and the GMap control.

ASP.NET
<wcp:GMap runat="server" id="gMap" Width="750px" Height="525px" 
  EnableClientCallBacks="True" OnMarkerClick="gMap_MarkerClick"/>

Again, note the EnableClientCallBacks and the OnMarkerClick event handler assignment.

StateQuarters.aspx.cs

When the page loads the GMap is centered on the United States. In a similar manner to the ColoradoCounties example, we query the database to get the two-letter state abbreviation and the latitude and longitude coordinates for each state. This data is bound to the GMap which results in a marker being added for each state. The Id of each marker is set to the abbreviation of the respective state.

C#
gMap.CenterAndZoom(new GPoint(-93.69141F, 40.11169F), 13);

DataSet ds = Cache["States"] as DataSet;
if( ds == null )
{
  ds = StateQuarter.GetStates();
  Cache["States"] = ds;
}
gMap.DataSource = ds;
gMap.DataMarkerIdField = "Abbreviation";
gMap.DataLongitudeField = "Longitude";
gMap.DataLatitudeField = "Latitude";
gMap.DataBind();

gMap.AddControl(new GSmallMapControl());

The code for the MarkerClick event takes the Id of the marker that was clicked and queries the database for the complete state information including: state name, state abbreviation, date of statehood, and the date the quarter was released.

C#
protected string gMap_MarkerClick(object s, GPointEventArgs pea)
{
  GMarker gm = s as GMarker;
  if( gm != null )
  {
    string gmInfoWindow = Cache[gm.Id] as string;
    if( gmInfoWindow == null )
    {
      StateQuarter sq = 
         StateQuarter.GetStateQuarterByAbbreviation( gm.Id );

This data is stored in a custom business object which is then transformed using the StateQuarter.xsl style sheet. The transformed HTML is passed to the GMarker.OpenInfoWindowHtml() method. This method creates the JavaScript that needs to be executed on the client side to open an info window over the marker that was clicked.

C#
string path = Server.MapPath("StateQuarter.xsl");
gmInfoWindow =
   gm.OpenInfoWindowHtml(GXslt.Transform(sq, path));

Finally, the info window JavaScript is cached for performance reasons and returned to the client which will actually display the info window. __Show Me__.

C#
      Cache[gm.Id] = gmInfoWindow;
    }
    return gmInfoWindow;
  }
  return String.Empty;
}

Handling a large number of markers

One of the biggest gripes I see posted regarding the Google Maps API is how slow maps become when adding a large number of markers. To most of those people I want to say "I'd ask Google for a refund of all the money you spent when you purchased the API". But seriously, JavaScript has its limits but we still need a way to represent a large number or markers.

There are a number of ways to do this including grouping markers within a certain proximity together at certain zoom levels. This can become a complex mathematical problem (for people much smarter than me to solve). The Accor Hotels example instead only loads the markers that are within the user's current viewing bounds, and removes them as the user pans to a different location. This doesn't completely solve the problem unless you restrict the user from zooming out. But the example below could be extended to only show the markers within the users viewing bounds and zoom level.

AccorHotels.aspx

__Show Me__

Image 3

This is the most complex example but possibly the most useful in terms of implementation in your own GMap applications. First, we'll start with the client side JavaScript. We implement three of the client side GMap events, GMap_AddOverlay(), GMap_RemoveOverlay(), GMap_MoveEnd(). Add/Remove Overlay are only implemented to give you a visual indication of when markers are added and removed as well as the number of markers visible within the current viewing bounds. In GMap_MoveEnd() whenever the user finishes panning the map, we loop through all of the overlays (or markers) and determine which ones are within the current bounds of the map. We remove any that are not within the viewing bounds.

JavaScript
function GMap_MoveEnd()
{
  var bounds = this.getBoundsLatLng();
  var numOverlays = this.overlays.length;
  for( var i=0; i<numOverlays; i++ )
  {
    var pnt = this.overlays[i].point;
    if( pnt.x < bounds.minX || pnt.y < bounds.minY ||
        pnt.x > bounds.maxX || pnt.y > bounds.maxY )
        this.removeOverlay(this.overlays[i]);
    numOverlays = this.overlays.length;
  }
}

The only other code on this page is the style sheet information, some labels, and the GMap control declaration. In this example we respond to the MoveEnd server-side event in addition to the MarkerClick event.

ASP.NET
<wcp:GMap runat="server" id="gMap" Width="750px" Height="525px"
  EnableClientCallBacks="True" OnMarkerClick="gMap_MarkerClick"
  OnMoveEnd="gMap_MoveEnd" />

AccorHotels.aspx.cs

The Page_Load() event for the Accor Hotels example is pretty boring. The only thing done here is to load a custom icon. A blue marker will be added to the map to represent a Motel 6 and the standard red marker will be added to represent a Red Roof Inn.

C#
private void Page_Load(object sender, System.EventArgs e)
{
  GIcon gi = new GIcon();
  gi.Id = "BlueMarker";
  gi.Image = new Uri(Global.BaseUri, 
                ResolveUrl("~/Advanced/blueMarker.png"));
  gMap.Icons.Add(gi);

  gMap.CenterAndZoom(new GPoint(-122.101944F, 37.401944F), 4);
  gMap.AddControl(new GSmallMapControl());
}

Whenever a marker is clicked, we use the Id of the marker to search for a custom business object that has the name of the hotel and the address of the hotel.

C#
protected string gMap_MarkerClick(object s, GPointEventArgs pea)
{
  GMarker gm = s as GMarker;
  if( gm != null )
  {
    string gmInfoWindow = Cache[gm.Id] as string;
    if( gmInfoWindow == null )
    {
      AccorHotel ah = GetHotelByName(gm.Id);

We then transform the business object and get the client side JavaScript needed to show the info window, just like the State Quarters example.

C#
      gmInfoWindow = gm.OpenInfoWindowHtml(HttpUtility.HtmlDecode( 
        GXslt.Transform(ah, path)));
      Cache[gm.Id] = gmInfoWindow;
    }
    return gmInfoWindow;
  }
  return String.Empty;
}

Things start getting interesting in the gMap_MoveEnd() event. This event loops through all of the hotels and adds markers to the map for every hotel that is visible in the current map bounds. First things first, we create a GOverlays object to temporarily store the new markers that will be added to the map.

C#
protected string gMap_MoveEnd(object s, GPointEventArgs pea)
{
  GOverlays newMarkers = new GOverlays();

Next, we get the current viewing bounds of our GMap from the property BoundsLatLng. BoundsLatLng is converted to a RectangleF struct. The reason we do this is to take advantage of all the functionalities built into RectangleF like Contains(), Intersect(), and Union(). We also get the BoundsLatLng from the previous call to gMap_MoveEnd() (saved to the Session). We do this so we don't add the same marker twice.

C#
RectangleF currBounds = gMap.BoundsLatLng.ToRectangleF();
RectangleF prevBounds = PreviousBounds;

Now we loop through all of the Hotels and find out if the current BoundsLatLng of the GMap contains the point at which the hotel is located. If so, and if the marker wasn't added previously, we add it to the list of new markers.

C#
foreach( object o in Hotels )
{
  AccorHotel ah = o as AccorHotel;
  PointF currPoint = ah.Marker.Point.ToPointF();
  if( currBounds.Contains(currPoint) )
  {
    if( !prevBounds.Contains(currPoint) )
      newMarkers.Add(ah.Marker);
  }
}

Finally, we update the PreviousBounds with the current BoundsLatLng, transform the list of new markers to JavaScript using the GMap.xsl style sheet, and return that JavaScript to the client where the markers will be added to the map.

C#
  PreviousBounds = currBounds;

  if( newMarkers.Count > 0 )
  {
    string path = Server.MapPath("../Scripts/GMap.xsl");
    string newOverlays = 
        GXslt.Transform(new GOverlaysWrapper(newMarkers), path, 
      gMap.GetXsltArguments());

    return newOverlays;
  }
  else
  {
    return String.Empty;
  }
}

I've encapsulated some of the functionalities in a few basic business classes, but the main thrust of this example is dynamically adding and removing markers based on the currently visible portion of the map. You can download the code to see the full implementation. The server-side MoveEnd handles the adding of the markers and the client-side MoveEnd handles the removal of the markers. __Show Me__.

Conclusion

In this article, we covered the advanced usage of my .NET GMap control. Now that you know how to use overlay data binding, client callbacks, and retrieve data about your GMap upon postback, the sky is the limit for the neat applications you can come up with.

In Part 3, I will discuss how I designed the GMap control and the reasoning for some of my design decisions (when XSL is involved there better be a damn good reason).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGoogle API3 Pin
Ahmad Abou Hamdh13-Oct-12 23:33
Ahmad Abou Hamdh13-Oct-12 23:33 
Questionabout sample data Pin
jrod.cp17-Sep-12 2:49
jrod.cp17-Sep-12 2:49 
Generalcopy of data example Pin
avontoun3-Jun-09 0:16
avontoun3-Jun-09 0:16 
GeneralGoogle Map,Problem with Marker Pin
0905v492-Mar-09 20:08
0905v492-Mar-09 20:08 
Generalusing a MySQL database. Pin
Timo W8-Jun-08 10:38
Timo W8-Jun-08 10:38 
Generaldisplay select marker s from my database Pin
kadvekar yogi12-Mar-08 19:01
kadvekar yogi12-Mar-08 19:01 
GeneralProblem in Icon Position Pin
Gugu Parmar17-Jan-08 8:10
Gugu Parmar17-Jan-08 8:10 
GeneralCounty and Zip code boundary data Pin
malludoubleoseven12-Apr-07 8:20
malludoubleoseven12-Apr-07 8:20 
NewsA new ASP.NET GMap control that WORKS Pin
Bill Pierce27-Dec-06 3:44
Bill Pierce27-Dec-06 3:44 
NewsCONTROL NO LONGER WORKS Pin
Bill Pierce22-Dec-06 4:56
Bill Pierce22-Dec-06 4:56 
GeneralGetting javascript error when loading the map Pin
rajus05077-Nov-06 12:06
rajus05077-Nov-06 12:06 
GeneralRe: Getting javascript error when loading the map Pin
Bill Pierce7-Nov-06 12:21
Bill Pierce7-Nov-06 12:21 
GeneralRe: Getting javascript error when loading the map Pin
rajus05077-Nov-06 19:48
rajus05077-Nov-06 19:48 
GeneralRe: Getting javascript error when loading the map Pin
rajus05078-Nov-06 10:58
rajus05078-Nov-06 10:58 
GeneralGIcon Pin
wfernandes23-Oct-06 2:06
wfernandes23-Oct-06 2:06 
NewsNew Sourceforge Project Pin
Bill Pierce22-Aug-06 5:27
Bill Pierce22-Aug-06 5:27 
QuestionMarker Trouble Pin
Member 29688511-Aug-06 10:29
Member 29688511-Aug-06 10:29 
JokeGrate Job Pin
roncansan23-Jul-06 15:22
roncansan23-Jul-06 15:22 
I find you work great a very well organize. I have already used both of your controls in my applications: the auto suggest, and the Google maps. I'm interested in a copy of the data Can you please provide me?

Also thanks to the guy at http://blogs.b-es.de/blogs/mh_blog/archive/2006/06/02/819.aspx to provide the upgrade to Google maps version 2

roncansan

QuestionClient Side Error Pin
Habeeb Matrix6-Jul-06 0:02
professionalHabeeb Matrix6-Jul-06 0:02 
GeneralOverlays are not working. Pin
Giriv1416-Jun-06 5:55
Giriv1416-Jun-06 5:55 
GeneralRe: Overlays are not working. Pin
Giriv1416-Jun-06 11:39
Giriv1416-Jun-06 11:39 
GeneralSize of page Pin
ABlokha775-Jun-06 23:11
ABlokha775-Jun-06 23:11 
GeneralA copy Pin
ABlokha775-Jun-06 1:35
ABlokha775-Jun-06 1:35 
GeneralFrom and To Polylines Pin
Mark McClean1-Jun-06 4:59
Mark McClean1-Jun-06 4:59 
GeneralError 200 Pin
ABlokha7730-May-06 2:45
ABlokha7730-May-06 2:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.