Google Maps API hands-on training, part 2

Go back to part one of the training.
Assumed knowledge:
HTML
Moderate to advanced Javascript understanding
CSS
XML
Google Maps API basic skills:

  • Initialization of map
  • Adding map controls
  • Basic adding of points, click event listeners
  • Display of map points from flat XML

Supporting files:

  1. Dynamic geocoding

    Users often will want to figure out how your data relates to their own geographic location. They want to search against an address or by clicking on a point on the map. To do this kind of functionality you’ll need to use Google’s gecoder function.

    1. Create an instance of the geocoder to use for your searches. This should be placed in the code outside of the function that you use to create your map so you can use it anywhere on the page.

      [js light="true"]var objGeocoder = new GClientGeocoder();[/js]

    2. Now let’s put the geocoder to use. Whereas most of our actions are called against the map instance (objMap), in this case we use the geocoder. Lets do a really basic manual address query that in any real situation you’d probably collect from an HTML form. You’ll see that Google takes the address and returns a point — formatted as a lat,long pair — if it finds something, and returns as undefined if it can’t geocode the address. In this case, if it does find an address, we’ll center the map on that point at zoom level 10. This code goes INSIDE the loadMap function, after the map has been instantiated.

      [js light="true"]var strTestAddress = "Your address here";
      objGeocoder.getLatLng(strTestAddress, function(objPoint) {
      if (!objPoint) {
      alert(strTestAddress + " not found");
      } else {
      objMap.panTo(objPoint,10);
      }
      });
      [/js]

    3. Another way to react to user activity is to ask Google for a lat-long pair based on a user’s click. This allows a user who doesn’t know the address for their desired location to still use a proximity feature. This involves applying an event listener to the entire map. When a user clicks on the map, we first make sure they’re not clicking on a marker. If they are, we let that marker’s earlier set click handler do its work and do nothing in our function. If it’s not a marker, we re-center the map and do whatever else we might want to. Often we’ll want to pass the geocoded point to another function.

      [js light="true"]GEvent.addListener(objMap, ‘click’, function(objMarker,objPoint) {
      if (objMarker) {
      }
      else {
      objMap.panTo(objPoint,10);
      var objNewMarker = new GMarker(objPoint);
      objMap.addOverlay(objNewMarker);
      objNewMarker.openInfoWindowHtml("Pssst, pass it on: " + objPoint);
      }
      });[/js]


  2. Polylines and polygons

    Markers are the most commonly used overlays in the Google Maps API, but polylines and polygons are next. Polylines are familar to most Google Maps users from the driving directions feature, but you can use them for other applications. Polygons are useful for showing regions and other non-point data.

    I know what you’re thinking: STOP! Please don’t think you can throw up a Google map showing all the counties in your state. It will be slow, slow slow — this is a major failing of the Javascript API to me, and is one of the main reasons we use Google Maps for Flash for complex polygon data.

    That said, polygons used one at a time or a few at a time work well. Let’s start by drawing a basic line:

    1. We’ll start by putting some points into an array:

      [js light="true"]var arrLinePoints = new Array();
      arrLinePoints.push(new GLatLng(33.5081, -112.2047));
      arrLinePoints.push(new GLatLng(33.5642, -112.1154));
      arrLinePoints.push(new GLatLng(33.5608, -112.0042));
      arrLinePoints.push(new GLatLng(33.5116, -111.9211));[/js]

    2. Then we’ll spit those points back out as a line. We specify an array that’s the source of the points, the color of the line, the line width, and the opacity of the line (0.1 to 1):

      [js light="true"]var objMyLine = new GPolyline(arrLinePoints, "#446891", 3, 1);
      objMap.addOverlay(objMyLine);[/js]

    3. Polygons work much the same, except that the last point in the array should be the same as the first one to close the polygon, and you specify a color and opacity for the fill color in addition to the stroke color and opacity:

      [js light="true"]var arrShapePoints = new Array();
      arrShapePoints.push(new GLatLng(33.4623, -112.1086));
      arrShapePoints.push(new GLatLng(33.4632, -112.0382));
      arrShapePoints.push(new GLatLng(33.4280, -112.0375));
      arrShapePoints.push(new GLatLng(33.4285, -112.1075));
      arrShapePoints.push(new GLatLng(33.4623, -112.1086));
      var objMyPolygon = new GPolygon(arrShapePoints, "#446891", 3, 1, "#99C1D8", 0.2);
      objMap.addOverlay(objMyPolygon);[/js]
  3. Clustering markers

    Many times you’ll have a lot of points that at wide zoom levels are too close together to distinguish. Using marker clustering is generally the best way to get around this. Google has built-in clustering functions, but we like one called ClusterMarker better because it has more flexibility. The approach works so well that we use ClusterMarker even when we’re not clustering icons because it makes it easy to trigger clicks to a particular point.

    You can download the plugin here:

    http://googlemapsapi.martinpearman.co.uk/downloads.php?cat_id=1

    And read more about using it here:

    http://googlemapsapi.martinpearman.co.uk/articles.php?cat_id=1

    1. For today’s example, in your HTML document’s <HEAD>, import the Javascript file that contains the clustering functions.

      [js light="true" htmlscript="true"]<script type="text/javascript" src="http://www.mikejcorey.com/nicar2010/ClusterMarker.js"></script>
      [/js]
    2. Create a custom icon to be used as your clustering icon. Since this icon has a different size and shape from the other icons from part one, we won’t base this on the baseIcon we created earlier. This should be defined with your other markers, outside the loadMap function

      [js light="true"]var objGroupIcon = new GIcon();
      objGroupIcon.image = "http://www.mikejcorey.com/nicar2010/images/severalicons-brown.png";
      objGroupIcon.shadow = "http://www.mikejcorey.com/nicar2010/images/severalicons-brown-shadow.png";
      objGroupIcon.iconSize = new GSize(41.0, 36.0);
      objGroupIcon.shadowSize = new GSize(57.0, 34.0);
      objGroupIcon.iconAnchor = new GPoint(20.5, 18.0);
      objGroupIcon.infoWindowAnchor = new GPoint(20.5, 18.0);
      [/js]

    3. Create a new ClusterMarker instance for your map. We’ll also tell ClusterMarker what icon to use to mark a cluster. We can also easily turn the clustering on and off by setting the second line to true or false. Note: For some reason my code display tags are freaking out after this point, so delete any backslashes in the code

      [js light="true"]var objMyCluster;
      var arrMarkerStorage = new Array();

      objMyCluster = new ClusterMarker(objMap, { clusterMarkerIcon: objGroupIcon });
      objMyCluster.clusteringEnabled = true;[/js]


    4. Instead of manually adding a marker using map.addOverlay, when our marker function runs each time we’ll push each marker into the array we just created.

      [js light="true"]function createMarker(objPoint,strHTML,strIconName) {
      var objMarker = new GMarker(objPoint,{ icon:strIconName });
      GEvent.addListener(objMarker, ‘click’, function() {
      objMarker.openInfoWindowHtml(strHTML);
      });
      arrMarkerStorage.push(objMarker);
      return objMarker;
      [/js]
    5. Since the ClusterMarker is now going to handle the placement of markers, we can delete or comment out the line inside loadMyLocations that actually places the marker.

      [js light="true"]
      //objMap.addOverlay(objMarker);
      [/js]

    6. Once our XML loop has finished running, we’ll tell cluster marker to go to work on the array of points we’ve stored up. Make sure this is outside and after your loop but still inside the loadLocations function.

      [js light="true"]
      objMyCluster.addMarkers(arrMarkerStorage);
      objMyCluster.refresh();
      [/js]