Google Deprecates Google Maps API for Flash

Well, I guess the decision that was made at ECU to use the JavaScript API, not the Flash API for Google Maps was ultimately the correct one.  According to the Google Geo Developers Blog, the Flash API deprecation announcement has been made.  At the time, the Flash API was not chosen due to lack of experience with ActionScript, plus the benefits of using HTML, CSS, and JavaScript for maps development.  Many advanced Google Maps I’ve seen have used the Flash API, so it will be interesting to see if the projects are converted to the JavaScript API, if they will be considered feature complete and not updated (Google will make bug fixes, but not add new features to the Flash API), or if work will continue with the knowledge that Google won’t add new features to the Flash API.

Using Google MyMaps as Live KML Data Sources

I felt very clever when I discovered how to get to the actual KML exported as part of the “View in Google Earth” feature of Google’s MyMaps area of Google Maps.  I still don’t know why I didn’t make the leap sooner from downloading the data to using it live in an application.  Although I figured it out on my own, I would have made the “discovery” sooner if I just had read Mike Williams Google Maps API Tutorial with a closer eye to detail.  My one excuse is that I didn’t originally use alternatives to Google’s own GGeoXML class.  Now that I’m collaborating with multiple map maintainers, I don’t want to go through the whole download/massage/publish cycle.  Instead I want to use the excellent editing and collaboration facilities of the MyMaps interface to allow others to keep things updated properly.

In the spirit of sharing, I want to walk you through the process of going from MyMaps to your own hosted map using that data as an overlay.

  1. Get a Google Account
  2. Sign in and Visit Google Maps
  3. Create your first My Map.  Click the My Maps link in the left side bar, then click Create New Map.
  4. Set the center point and map display type using the normal maps interface.
  5. Edit the map by using the Edit button.
  6. Save the map using the Save button.

Note that you can add polylines and markers to your map, as well as edit the marker information as plain text, rich text, or HTML.  You can pick icons for your markers or add your own.  Note that adding your own doesn’t add to a data store, but uses a URL reference.  If you don’t maintain or control the server hosting the image, you won’t be able to guarantee that your custom marker will always be available.

When you’re happy with your map, use the View in Google Earth link above the map display itself (between RSS and Print).  Instead of letting Google Earth load the file, save it to your computer.

To help with my examples, I created a Demo Map Showing East Carolina University in my public Google account.  The link in my blog post was created by using the handy Link link within the interface.

You’ll note that the file size of the kml file is very small, which if you’ve created many markers with detailed information will seem confusing.  This XML doesn’t contain your data, but contains a URL to your data.  For instance, here is the contents of the KML file exported for my demo map:



  Demo Map Showing East Carolina University
  

The important feature of this file is the href element inside that link element. That’s the URL we want to our actual KML data. If we with to use it with GGeoXML, we can just replace the entities with their single character equivalents and construct the layer:

geoXml = new GGeoXml("http://maps.google.com/maps/ms?ie=UTF8&hl=en&oe=UTF8&msa=0&msid=113918317131300723802.00047c45b431d9dc64cb7&output=kml");
map.addOverlay(geoXml);

That’s all there is to it. Now if you or any collaborator edits the map, the next time it’s reloaded the GGeoXML class will pick up the latest version. Next time I’ll discuss using the much more advanced Geoxml class. It requires one more bit of work because it cannot load data from URLs that are not within the same domain.

TabbedMaxContent with Multiple Points

Last time I showed how to use the TabbedMaxContent from Google to display large amounts of possibly dynamic data. The example I used only had one GMarker, though, which isn’t especially useful. Most map users want to have multiple points. So, with that in mind, here are some of the techniques that you need to use to handle multiple markers that use TabbedMaxContent.

First, we need some latitude, longitude, and label triples. Since this example isn’t using AJAX to retrieve the information, we’re going to use some fixed JavaScript assignments:

pointdefs[0] = new Object;
pointdefs[0].latitude  = 35.6058733469;
pointdefs[0].longitude = -77.3629331589;
pointdefs[0].label     = "Austin Building";

pointdefs[1] = new Object;
pointdefs[1].latitude  =  35.606426;
pointdefs[1].longitude = -77.364464;
pointdefs[1].label     = "Rawl Building";

pointdefs[2] = new Object;
pointdefs[2].latitude  =  35.613659;
pointdefs[2].longitude = -77.370827;
pointdefs[2].label     = "Cotanche Building";

There’s nothing special here, we’re just creating an array of objects to make life easier. Each object has three attributes, the previously mentioned latitude, longitude, and label. So, we have objects with the information we need, how do we turn these into useful map objects? We will need to loop over the basic geo data and:

  1. create GPoints (which are Google’s latitude/longitude classes)
  2. create a Gmarker from the previous GPoint
  3. create two MaxContentTabs for the GMarker
  4. assign a click event handler to fire off the MaxContentTab open method

All of this functionality can be encapsulated into a single function that returns the constructed GMarker:

function createMarker(point, index, pointdef) {
	var tab1html;
	var tab2html;
	var tab1;
	var tab2;
	var tabs = [];	
	var marker = new GMarker(point);
	
	tab1html = '

Point ' + index + ' tab 1

'; tab1 = new MaxContentTab('Tab 1', tab1html); tabs.push(tab1); tab2html = '<p>This is the second tab. Here is a random valued Google Chart to give an example of interesting content</p><p id="tab2image"></p>'; tab2 = new MaxContentTab('Tab 2', tab2html); tabs.push(tab2); // HTML for minimized and maximized state of the TabbedMaxContent window var regularInfo = pointdef.label + '<br/ ><a href="javascript:void(0)" onclick="javascript:map.getInfoWindow().maximize()">more information ... </a>'; var summaryInfo = '<a href="javascript:void(0)" onclick="javascript:map.getInfoWindow().restore()">less information ... </a>'; GEvent.addListener(marker, "click", function() { marker.openMaxContentTabsHtml(map, regularInfo, summaryInfo, tabs, { maxTitle: 'Detailed Information'} ); }); return marker; }

In the code above, we’re passing in a GPoint, an index (for labeling purposes), and a pointdef (one of the objects in our array). Other than handling an object’s attributes for the label, the code is pretty much the same as our previous MaxContentTab example. Using a function gives us the isolation of the constructed GMarker to prevent the JavaScript engine from reusing the string data and giving us the proper results when we click each GMarker.

All that remains is a driving loop that calls the function:

			for (var i = 0; i < pointdefs.length; i++) {
				// Single GLatLng instance
				var point;

				point =  new GLatLng(pointdefs[i].latitude, pointdefs[i].longitude);			
				marker = createMarker(point, i, pointdefs[i]);

				// Add this marker to the map itself
				map.addOverlay(marker);
				markers.push(marker);
			}

In real production code, you'd want to create a variable to contain the array length, rather than having it evaluated for every element of pointdefs. In addition, you could move the GPoint assignment to the createMarker function, rather than passing it in as I've done here. The point creation in the loop, rather than the function, is a remnant of earlier examples I created for myself.

The final line of code in the loop, markers.push(marker), is not needed for our example in this posting. It's a taste of things to come, an array to allow for a sidebar that gives you alternate means of accessing your map markers.

Update: I almost forgot my link to a live example that puts all the code together.

Adding Dynamic Behavior When Using TabbedMaxContent

Last time I promised a continuation of the series describing how I used the Tabbed Max Content JavaScript class for some Google Maps at ECU. One of the inspirations I used was the many examples in the Google Maps API Demo Gallery. Specifically, I used code snippets from the Tabbed Max InfoWindow + Dynamic Content example. Thanks to Nianwei Liu for the Tabbed Max InfoWindow example posted there.

The simple example from my last posting didn’t need any dynamic actions; all of the rendering was handled by the Tabbed Max Content itself. If the contents of the Tabbed Max Content may change, though, we need additional actions.

GEvent.addListener(map.getTabbedMaxContent(), 'selecttab', function(tab) {
   switch (tab.getLabel()) {
      case 'Availability':
         // Generate the dynamic HTML as strings
         // Set the html content  of the div, p, or other block element to be that string
      break;
   }
});

In the code above we have the outline of our basic tab selection event listener. The method that will fire is selecttab, generated by the map’s current Tabbed Max Content instance. We only have to do this once in the entire map, not once per marker.

I have created a simple example that generates a random value and sets the image data for a Google chart to graph the value:

http://www.carolinamantis.com/demo/tabbedmaxcontent/ex02.html

My example adds this block of code for the dynamic aspect:

GEvent.addListener(map.getTabbedMaxContent(), 'selecttab', function(tab) {
   switch (tab.getLabel()) {
      // We could also have used case 1 below (tabs are numbered from 0 and increase as they go left to right)
      // Here I choose to switch on the actual text of the tab, note it is enclosed in quotes but the numeric 1
     // is not.
     case 'Tab 2':
        // Everything needed for a Google chart but the final value
        var url = 'http://chart.apis.google.com/chart?chs=170x170&chtt=Power%20Reading&cht=gom&chd=t:';
        // Generate a random number between 0 and 100
       var randomnumber=Math.floor(Math.random()*101);
       url += randomnumber;
       var htmlsrc = '';
       // Use jQuery to set the HTML for the paragraph.
       $('#tab2image').html(htmlsrc);
    break;
    }
});			

If you visit the example page, you can click on the point and then expand the Tabbed Max Content. Every time you switch between Tab 1 and Tab 2, a new random value will be generated and the graphic will be updated.

Next time I will post about how to manage the case where you have many points on your map, and must generate separate dynamic content on a tab for each one.

Using TabbedMaxContent with Google Maps

While working on a project to show general purpose computer labs at East Carolina University, I needed to combine several techniques, libraries and technologies.  This post is about my experience with the TabbedMaxContent JavaScript class from the gmaps utility library.

The examples and API reference certainly helped, but it took more effort to figure out how to handle some of the dynamic interactions needed in our lab map.  The big challenge was separating the functionality to display the “static” information in the tabs from the dynamic information.

Whenever I’m putting together a project like the lab map, I like to create bite sized examples of the parts working independently. Then combining the techniques prevents trying to debug everything all at once. With that in mind, I wanted my own example of a Google Map with a single marker point using a TabbedMaxContent. I have put together a simple example at http://www.carolinamantis.com/demo/tabbedmaxcontent/ex01.html

The beginning of this code uses the Google loader to load both jQuery and the Google Maps API. Why use your own bandwidth (and your users) when they can use a previously cached copy?


	google.load("maps", "2");
	google.load("jquery", "1.3.2");

Note that where I have PUTYOURMAPKEYHERE you need to use the Google Maps key generated for the domain/IP address you’re using to host the map. If you only want the Google AJAX Library APIs and no map, then you can leave the query string (including question mark) out of the URL.

You also need to include the source for the TabbedMaxContent class. I put mine in my document root, but please host your own if you are going to use it.


Continuing on, we have the meat of the TabbedMaxContent and Gmarker creation:

			tab1html = '

This is the first tab. You have few limits to the HTML that may be put here

'; tab1 = new MaxContentTab('Tab 1', tab1html); tabs.push(tab1); tab2html = '

This is the second tab. Here is a fixed Google Chart to give an example of interesting content

'; tab2 = new MaxContentTab('Tab 2', tab2html); tabs.push(tab2); // HTML for minimized and maximized state of the TabbedMaxContent window regularInfo = '
more information ... '; summaryInfo = 'less information ... '; // Add a listener for the click event on the marker itself GEvent.addListener(marker, "click", function() { marker.openMaxContentTabsHtml(map, regularInfo, summaryInfo, tabs, { maxTitle: 'Detailed Information'} ); }); // Add this marker to the map itself map.addOverlay(marker);

Beyond the usual point/marker/map overlay creation, the difference here is the additional variables for the HTML tab contents, the MaxContentTab instances (which in essence are the tabs), and the addition of a listener for when the marker is clicked. When our marker is clicked, we want it to invoke openMaxContentTabsHtml, a method which is part of the TabbedMaxContent API. We pass to this method our map instance, the HTML string for the minimized information, the HTML string for the maximized information, the array of MaxContentTabs, and and additional configuration hash that in our example specifies a title for when the TabbedMaxContent is maximized.

Note that you can have the marker creation in a loop and reuse the variables point, marker, tabNhtml, and tabN. The lab example, which is more sophisticated in that it is using some jQuery AJAX loading, does just that.

For now, I’ll leave off with some changes that I would make if I were to redo my code. I’d probably use jQuery to get the map div, as opposed to the generic getElementById. I would also try to better use the jQuery event model and management system rather than the GEvent system. For now, these techniques work and work cross-browswer. At the end of the day, that’s what’s important.

Next time, I’ll go deeper into TabbedMaxContent with jQuery’s AJAX APIs to handle a retrieved marker set and to have dynamic data within the tabs.

Until then…