Using Google Maps Inside of Flash CS3: Part 3

If you’ve been with me over the past 2 tutorials, we made a basic Google Map application in Flash CS3 in part 1 and in part 2 we created a database full of locations plus made a service using AMFPHP that allows us to access that data.

In this final part, we’re going to jump back into Flash and build upon that original map from part 1 and hook it up to the database from part 2.

Let’s jump right in and look at what we’ll be building.

Live version: http://jmx2.com/supergeekery/sg_map_example_part3.php

Files: Download the example FLA. (Updated 12-17-2010)

As I mentioned in part 1, I’m hosting this map on my business’s domain, jmx2.com, instead of SuperGeekery.com because SuperGeekery.com uses an security certificate, notice the https in front of the URL instead of the standard http, and Google Maps does not work with secure connections presently. (The only reason SuperGeekery has a security certificate is because I wanted to see if I could make it work. There is no other reason for it.)

After looking at the example, you’ll notice some new elements added since part 1.

  1. A text field component where a user will enter an address or zip code.
  2. A button component for executing the search.
  3. A data grid component.
  4. A couple text fields, there for testing purposes, displaying longitude and latitude.
  5. A text field for displaying feedback from the data request, displaying the number of results, or error messages, as needed.

Let me show you around the application.

Do some test searches. Type in your zip code. Did you find some locations? Try these.

123456 - This doesn’t return any Geo lookup from Google. Notice the feedback message shown.

62665 - The database returned no results. Notice the feedback message.

10003 - There are store results. Now the feedback displays how many store it found.

Now that you’ve got some store results, they’re displayed in the datagrid. Click on one of them. See how the map recenters itself to that location? Nice. Click on any of the markers and you’ll see information on that location. Cool, right?

Since the ActionScript is getting pretty long now, I will only cover some highlights in my post here, but I use many comments in the code that should answer most of your questions. The full ActionScript (which for this example all resides on frame 1 of the example file) will be included at the end of this post.

If you downloaded the FLA, which I encourage you to do, you’ll see that the AS starts by importing many more chunks of code than before. You might be familiar with many of the normal Flash ones, but ones from Google might be completely new. The most useful one is probably the Geocode ones from Google. They’ll help us translate a street address or zip code into a longitude and latitude coordinate. The Marker and InfoWindow ones give us control of the identification pieces within the map.

import com.google.maps.services.ClientGeocoder;
import com.google.maps.services.GeocodingEvent;
import com.google.maps.overlays.Marker;
import com.google.maps.overlays.MarkerOptions;
import com.google.maps.InfoWindowOptions;
import com.google.maps.controls.ZoomControl;
import com.google.maps.styles.FillStyle;
import com.google.maps.styles.StrokeStyle;

The next big update in the code is in the onMapReady function which we trigger, unsurprisingly, when we ‘hear’ the event of map becoming ready. In addition to moving the map further down on our stage than before, our updated onMapReady function now sets up our gets our geocoder function ready.

We assign the variable ‘geocoder’ a new instance of a ClientGeocoder object. Next, we need to activate the search button, which is just a button component, by assigning an event listener that will trigger a doGeocode function.

The doGeocode function will make the geocoder return either failure or success and we tell it what to do in each case.

Finally, we wrap up the map being ready by creating the datagrid component and adding it to the stage. (You might be asking yourself why I create this component with code when I have other components on stage from the beginning. This comes from the final product I was creating which displayed an instruction screen first, then only displayed results in a datagrid when they were found. Basically, I only created the datagrid when needed and that is now part of this example.)

geocoder = new ClientGeocoder();
searchbutton.addEventListener(MouseEvent.CLICK, doGeocode);
geocoder.addEventListener(GeocodingEvent.GEOCODING_FAILURE, geoFailed);
geocoder.addEventListener(GeocodingEvent.GEOCODING_SUCCESS, geoSuccess);
makeDataGrid();

Let’s now check out the doGeocode function. It’s pretty simple. It takes the text that the user put in the text field on the screen and calls the geocode function on our geocoder instance. In the feedback text area, we’ll tell the user we’re searching the database. Lastly, just in case there is data in the datagrid from an previous search, we’ll get rid of it by removing all that old data.

function doGeocode(event:Event):void {
 geocoder.geocode(address.text);
 feedbacktext.text = "searching….";
 dg.dataProvider.removeAll();
}

We have to write our functions on what to do when geocoder responds. The first one is easy. If it fails just tell the user it failed using the feedback text box.

function geoFailed(event:GeocodingEvent):void {
 feedbacktext.text = "We couldn't find that location in the Google database of addresses.";
}

If geocoder responds with a success message, we’ve got some more work to do. First, we need to look inside the event’s response to see how many placemarkers were returned from the request to Google’s geocode service, if it is more than 1, we’ll take Google’s first response as the most likely to be correct. Before we place a marker on the map to pinpoint that place though, we should clear off the map in case this is not the first time the map is being used by calling the clearOverlays function on the map object.

Now we’ll set the map’s center point to the first placemarker our event has. In this setCenter function, we also set the zoom level, which I’ve set to 11 in our example. To set a marker in this center position, we’ll use the Marker object from Google.

Before we call the addOverlay function, which is what will make the marker visible on the map, set a listener for MapMouseEvent.CLICK to open a window that will show the the content of the variable we’ve called html, which the current text from the address text field on the screen. We’ll use the longitude and latitude text fields on the screen to display that information for our first placemark, using point.lat() and point.lng(), for us to see. You’ll probably want to just use variables for this later. I’m just displaying them here for testing purposes.

Note: On 12-16-2010 I got a note from Amit asking me some questions about this example and on line 15 below, he helped me find that I had swapped mylat and mylng. They have been corrected below and in the FLA you can download from this page. Thanks, Amit!

function geoSuccess(event:GeocodingEvent):void {
 if (event.response.placemarks.length > 0) {
  map.clearOverlays();
  map.setCenter(event.response.placemarks[0].point, 11);
  marker= new Marker(event.response.placemarks[0].point);
  var html:String = "" + "Your location:" + " 
" + address.text;
  marker.addEventListener(MapMouseEvent.CLICK, function(event:MapMouseEvent): void {;
  map.openInfoWindow(event.latLng, new InfoWindowOptions({tailAlign: InfoWindowOptions.ALIGN_CENTER, contentHTML:html }));
  });
  map.addOverlay(marker);
 }
 mylat.text = event.response.placemarks[0].point.lat();
 mylng.text = event.response.placemarks[0].point.lng();
 gw.call("GetLoc.getLocation", res, mylat.text, mylng.text, '25');
}

Before we’re done with the onSuccess function, we have more more very important line of code to write. We need to access the AMFPHP code we wrote in part 2. We do this with a call to the AMFPHP gateway. In part 2 we wrote a service called GetLoc that had a function called “getLocation”. We pass it the longitude and latitude that we received from the call to the geocode function. The 25 here represents the radius we’re searching within for store locations from our database. The “res” in this line refers to the variable which holds the Responder object which we’ll set up next.

If you’re checking out the full code from the example file, you’ll see you need to update your gateway information with your server’s information. This is covered in part 2.

To set up your responder, define the variable and assign the functions to call upon a successful request to the database, onResult, or a failed request, onFault.

var res:Responder = new Responder(onResult, onFault);

In the onResult function definition, we need to deal with the results from the call to AMFPHP gateway. Figuring out how to do this is a little tricky. Unfortunately for us using CS3, the data is returned as an array collection. This is easy to deal with in Flex, but Flash, for some reason, doesn’t have the same easy way of dealing with this type of return. Luckily Lee Brimlow tells you exactly how to deal with this by using debugging in Flash. If you want the details be sure to watch his tutorial here. If you don’t want to know the details, just know that responds.serverInfo.initialData is where you need to look for your results. Using that information, we see how many results we have and respond accordingly and update the feedback text.

To wrap up this function, we cycle through the results and add a marker to the stage for each one. While we’re cycling through the results, we also add each item to the datagrid and add a listener in case the user clicks on that datagrid item.

function onResult(responds:Object):void {
	var theresults:Array = responds.serverInfo.initialData;
	// let's show the user how many results we found
	if (theresults.length > 0) {
		if (theresults.length == 1) {
			feedbacktext.text = "There is "+ theresults.length + " store in your area.";
		} else {
			feedbacktext.text = "There are "+ theresults.length + " stores in your area.";
		}
	} else {
		feedbacktext.text = "Sorry, but there were no stores in your area. Perhaps search for a larger town near where you live. ";
	}
	for (var j:uint=0; j<theresults.length; j++) {
		var storeName:String = theresults[j][1];
		var storeAddress:String = theresults[j][0];
		// add each item to our datagrid
		dg.addItem( { nameCol: storeName, addCol: storeAddress, lngCol: theresults[j][2], latCol: theresults[j][3], distCol: theresults[j][4] } );
		// this Event.CHANGE operates like a Mouse CLICK event, and calls gridItemsSelected function to recenter the map with the lng / lat of the selected row's address
		dg.addEventListener(Event.CHANGE, gridItemSelected);
		createMarker(new LatLng(theresults[j][2], theresults[j][3]), storeName, storeAddress);
	}
}

function createMarker(latlng:LatLng, name:String, address:String):void {
	// the markers created in this function are for showing store locations, not for showing the initial address Google gave us. So let's change how they appear using the MarkerOptions Google gives us.
	var mylocalmarker= new Marker(
	      latlng,
	      new MarkerOptions({
	                  strokeStyle: new StrokeStyle({color: 0x987654}),
	                  fillStyle: new FillStyle({color: 0x223344, alpha: 0.8}),
	                  radius: 9,
	                  hasShadow: true
	      })
	  );
	// these variables hold formatting information for the info windows
	var html:String = "<b>" + name + "</b> <br/>" + address;
	mylocalmarker.addEventListener(MapMouseEvent.CLICK, function(e:MapMouseEvent):void {;
	mylocalmarker.openInfoWindow(new InfoWindowOptions({fillStyle: { color: 0xFFFFFF, alpha: 0.9 }, tailAlign: InfoWindowOptions.ALIGN_CENTER, contentHTML:html}));
	});
	map.addOverlay(mylocalmarker);
}

function onFault(responds:Object):void {
	map.setSize(new Point(445, 500));
	for (var i in responds) {
		feedbacktext.text = "Error: Responder didn't work from your gateway and returned an error.";
	}
}

function gridItemSelected(e:Event) {
	mylng.text = "long: " + e.target.selectedItem.lngCol;
	mylat.text = "lat: " + e.target.selectedItem.latCol;
	// pan to center map on selected location
	map.panTo(new LatLng(e.target.selectedItem.lngCol,e.target.selectedItem.latCol));
}

startMyMap();

Update: I’ve now posted all 3 tutorials for this series on SuperGeekery plus an update about Google releasing the component with official support for the Flash IDE: Here are the links for all parts: Part 1, Part 2, Part 3, and the Update Post.

Comments on this post.

This is great! Can’t wait to start using it.

I’m trying to add a listener to the grid Items so when a user clicks an Item in the grid, not only does it center that Item it also shows the Info window for that Item.

Any help would be greatly appreciated.

COMMENT:
Hey Bart,

When I was building the store locator, that was on my “to do” list too. It fell off my list as my deadline approached though. If I dig back into it, I’ll post it here. If you get it working, please post a comment with your solution. I’m glad you’re excite by building your own. I hope this is helpful to you.

Best,

John

COMMENT:
John,

I am having trouble using the map on some computers. Any Idea what might be causing this issue.

All computers are XP sp3 with IE7 and all have the latest addition of flash play installed. It works fine on most computers but on some computers when I enter a zip code to search it just says, “Searching” and never loads the data. Like I say using the same zip code to search on most computers it works fine.

Thanks for your help in advance.

Bart

COMMENT:
Can you see what version of the Flash Player you have on all those machine? Are they all the same?

Here’s Adobe’s link that will show you what Flash version a machine has:

http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_15507

-John

COMMENT:
Yes they are all version WIN 10,0,12,36

COMMENT:
Here is a link where I have it setup even though I am still in the testing stage it is live.

http://www.painlessperformance.com/dealers/index.php

COMMENT:
I have a hunch.

On the computers that you didn’t have it working on, did you type in the URL? Maybe:

http://painlessperformance.com/dealers/index.php

Instead of:

http://www.painlessperformance.com/dealers/index.php

I notice that both URLs are working, although they are technically different.

If I use the www-less URL, I get the map, but “Searching…” never results in a result generated from the database. When I use the URL *with* the www, it works fine.

COMMENT:
Also, NICE WORK! I was very excited to see it working for you! That makes my day.

COMMENT:
John, you are brilliant! that was the problem. I didn’t even notice that because I was clicking on short cuts to get to the page.

I still have a lot to do to get it to do exactly what I want it to do, but this is excellent work you’ve done here.

I wish you would dig that back out and get back to work on it. sure would make my life easier.

Thanks so much for your help!

Bart

COMMENT:
Bart, glad I could help. One thing I see you accomplished was to make the info window pop up when you select the appropriate item in the data grid. What did you do there?

COMMENT:
Didn’t really accomplish anything yet. I’m still working on it. It’s really sloppy. If you click on a store location in the datagrid and then click back on the home location marker you’ll see what I mean. Also I’m trying to get the info window above the marker when clicking on the datagrid like it is when you click the marker.

If you want I can email the fla to you if you like to take a look and see if you can figure out a better way of doing it. Oh, and how did you round the miles down to 2 decimal places?

Thanks again for your help,

Bart

COMMENT:
I wrote a function called “twoDec” which will take any number and return it to just 2 decimal places. Here that is:

function twoDec(aLongNum:Number):Number {

   var times100:Number = aLongNum * 100;

   times100 = Math.round(times100);

   var div100:Number = times100/100;

   return div100;

}

By John Morton on Nov 21 2008

On Dec. 16, 2010, I got an email from Amit. He help spot an error I mentioned above about some swapped variables and those have been updated above.

Another issue he had was the error:
Error #2044: Unhandled NetStatusEvent:. level=error, code=NetConnection.Call.BadVersion

Servers are not all set up the same way. I hadn’t seen this error before, but in an exchange of several email, he found a solution that might help out someone else that might help someone else following along here.

In his gateway.php file he added:

setLooseMode(true)

That line solved the problem. If you’re having a similar error, give that a try. Cheers.

By John Morton on Dec 17 2010

Leave Your Comment:

name:

email:

location:

url:

your comment:

Remember my personal information

Notify me of follow-up comments?

Twitter Feed

John Morton talking on Twitter




Meanwhile on Instagram… //

My latest shot from Instagram.
KISS #pinball. Didn't love the game but the art is awesome. Bally... That's a classic logo too. #design