At A Glance Main Projects Tutorials Resume

Contact


Email: palen1c at gmail.com




Migrating to Javascript from the Google Maps API for Flash Using Adobe AIR

Fri, 2 Dec 2011 22:59:58 EST

Running the google maps v3 javascript API in Adobe AIR.
Update: 6/19/2016

This technique no longer works. Terry Corbet(tcorbet at ix.netcom.com) was nice enough to do the extensive research needed to get around the no longer working htmlloader method.
Straight from Terry, this outlines the general process needed to get the technique to work.







"A. Cease using the HTMLLoader; use, instead StageWebViewBridge.

B. StageWebView provides no capabilities for communicating -- in both directions, as you example requires -- between the desktop Actionscript and the Javascript in the rendering engine. StageWebViewBridge fills that gap with methods permitting call from the desktop to the browser and from the browser to the desktop.

C. That said, for reasons similar to the reasons you took the approach of using the HTMLLoader's loadString (text) in lieu of load (urlRequest). The composite that will be sent to the rendering engine will be assembled from:

1. The template code which AIR applications will most easily access by using the {EMBED] capabilities of the mxmlc compiler.

2. The Google Map Javascript code which must be separately fetched via URLLoader using your developer's API Key. It is in trying to rely on the rendering engine to fetch/load the Google Map API that the fatal SYNTAX_ERR: DOM Exception 12 arises. by performing the merge ourselves, we are able not only get our hands on something to look at to debug, we are able to make sure the merged code is valid, by simply saving our intermediate work and letting the stand-alone browser load it and render it.

3. What remains is to get the StageWebViewBridge.js code into our document, and that is handled by simple initialization code within the API provided."

After over 500 hours of work; Terry had more to report: "I found it impossible to use the toolkit solutions involving a StageWebView that relies up the "Native" rendering engine and the StageWebViewBridge library that was crafted in 2011 and for which there is no on-going support. Rather, let me more precisely restate that: I was able to use that toolkit to solve the problem on a Windows 7 Desktop which means that the rendering is being accomplished by IE 9 of some version. That code, with or without minor tweeks that might be expected and acceptable in order to deliver the application to a MacOS desktop, simply would not work.

Eventually, in order to try to coerce some diagnostic output from Safari's handling of the StageWebView, I had to dig into the StageWebViewBridge code, line-by-painful line, with zillions of trace statements on the client side and zillions of alert statements on the browser/server side. In so doing, instead of just being able to treat StageWebViewBridge as an opaque layer of software that would keep its promise in letting me "bridge" ActionScript to JavaScript and back again, I had to understand what the author had done, how he had done it, and as much a I could infer of why he had done it.

Actually, when approached that way, almost any sane person would simply have given up once it became clear that SWVB had never been developed for nor tested to determine whether or not the code would work when the rendering engine was Safari on a Mac. Perhaps the author had tested that configuration and given up, or perhaps since the focus was already on iPhones and iPads, he never gave it a thought.

So, once I began to understand the original code, I determined that I really had no interest in over 90% of that code. The 10% I needed would handle the two-way communication between ActionScript and JavaScript, so I culled that out and just used the code, not as separate ActionScript and JavaScript libraries, just as additional logic in a few methods of the client and server sides of the communication protocols.

Finally, I was able to get a common code base that successfully operated on Windows and MacOS workstations. There is clearly code that we cannot see inside the Apple implementation of its minimal and begrudging support of StageWebView that makes it virtually impossible to get the kind of results that you get with the Microsoft support of StageWebView. Finally, I found that under some circumstances, Safari silently -- no messages no documentation -- nulls out the reference to the StageWebView, thus causing the updates to the screen to stop. So, when I say I have succeeded, I guess the more correct statement is that I have isolated the problem and implemented a single-line workaround which causes the Mac version of my application to have about a half-second flicker for a screen refresh that is not necessary and does not occur on the Windows version. That line of code recreates the StageWebView object, and re-establishes its viewPort after a Click Event on the Map which is used to send the Lat-Lng coordinates to the ActionScript client."

Original Article from 2011:

Google recently depreciated the maps API for Flash, which according to their terms of service looks like will be completely dead within three years. Despite everyone freaking out about Adobe's recent announcements, Adobe AIR will still be supported across several platforms. In the museum industry I've used Adobe AIR on many projects. Utilizing certain techniques available in AIR you can implement the fully supported Javascript V3 Google Maps API and have close to the same functionality as the Flash API.

The Adobe AIR HtmlLoader classes provide access to a built-in webkit browser that has Javascript rendering capability. The HtmlLoader is the avenue that I used to implement the Javascript V3 library. Using AIR you can make calls to Javascript functions, and Javascript can in-turn preform call-backs into your Actionscript functions if you set them up properly and pay consideration to the AIR security sandbox. The HtmlLoader can properly render the Google Maps in the browser, so there really isn't much work that needs to be done.

I'm going to walk you through how to get an example working that uses the directions service to request directions between two points with a way-point between them. I wanted to use this service because I initially had trouble with the google.maps.DirectionsRoute object because it contains a lot of great data and I made a tiny mistake when initially trying to parse it in AS3.

The initial geocoded location of a museum in Texas.
The methodology I use loads a HTML file into AIR/Flash dynamically during run-time then orders the HTML Loader to render that. I took this approach because I wanted an intermediate level of code in case the Javascript API ever changes.

Step one is to get a html file working that that will render the Google Maps API. I have this html file set so it will render the latitude and longitude of one of my favorite museums I've been lucky to work on, the National Museum of the Pacific War in Fredericksburg, Texas, U.S. When the page loads, the Javascript function initialize is called, which uses the geocoding service; which in Javascript returns the results, where I extract the latitude and longitude and render the map. This initial geocoding is all done in the Javascript.

Here is an excerpt from the html of some of the javascript functions that help get the process going:

  1.   var map = null;
  2.   var geoCoder = null;
  3.   var directionService = null;
  4.   var directionsDisplay = null;
  5.  
  6.   // First, make a request using the cool geocoding service to get the latitude and longitude
  7.   function initialize() {
  8.     var initialAddress = "National Museum of the Pacific War 340 East Main Street, Fredericksburg, TX 78624";
  9.          
  10.     geocodeStringAddress(initialAddress);
  11.    
  12.   }
  13.   function initializeMap(_LatLon) {
  14.         var myOptions = {
  15.               zoom: 8,
  16.               center: _LatLon,
  17.               mapTypeId: google.maps.MapTypeId.ROADMAP
  18.             };
  19.            
  20.             map = new google.maps.Map(document.getElementById("map_canvas"),
  21.                 myOptions);
  22.   }
  23.   <!-- Handles the result of a geocode -->
  24.   function handleGeocodeResult(results, status) {
  25.  
  26.       if (status == google.maps.GeocoderStatus.OK) {
  27.        
  28.             try {
  29.                 // This calls flash with the resuls check your traces
  30.                 window.JSnewGeocodeData(results);
  31.                 // Call this so we get a map going
  32.                 initializeMap( results[0].geometry.location );
  33.             } catch (err) {
  34.                 // Sink the error that was trying to call an AIR function
  35.             }
  36.       } else {
  37.             try {
  38.                 // Calls a function in AS3 that references the error
  39.                 window.JSgeocodeError(status);
  40.             } catch (err) {
  41.                 // Sink it
  42.             }
  43.       }
  44.   }
  45.   <!-- Can call this from AIR using the DOM for geocoding stuff..weeeeee -->
  46.   function geocodeStringAddress(_stringAddress) {
  47.     if(geoCoder == null) {
  48.         geoCoder = new google.maps.Geocoder();
  49.     }
  50.     geoCoder.geocode( { 'address': _stringAddress}, handleGeocodeResult );
  51.   }


After you get things initialized, the next step is to fill in the rest of the Javascript functions needed to work with your desired maps functionality. Make sure your javascript is error free, then we can go over to the Flash/AIR side of things. The full html file(available in the sample download) includes the rest of the javascript functions that are needed to work with the cool Directions service.

In Flash/AIR, I first load the html (named GoogleMapsV3_JSFunctions.html) as a big string using a URLLoader.
  1. private var AbstractDataLoader:URLLoader = new URLLoader();
  2. private var theBrowser:HTMLLoader;
  3.  
  4. //..... further down in the code
  5. AbstractDataLoader.addEventListener(Event.COMPLETE, HandleData, false, 0, true);
  6. // Start loading the html
  7. AbstractDataLoader.load(new URLRequest("GoogleMapsV3_JSFunctions.html"));
  8.  
  9. private function HandleData(e:Event):void {
  10.      e.currentTarget.removeEventListener(Event.COMPLETE, HandleData);
  11.  
  12.     var notXML:String = e.target.data;
  13.     trace("Text loaded - rendering in browser");
  14.     theBrowser.loadString(notXML);
  15. }

When the html is loaded as a plain string, I tell the htmlLoader to render it using the loadString method.

The way you call javascript from AIR and AIR from Javascript are as follows:

In Flash to call Javascript and allow callbacks, you preform certain operations on the HTMLLoader instance. To allow the callbacks, you need to set them up:
  1. // If set to True wont load external scripts in html
  2. theBrowser.placeLoadStringContentInApplicationSandbox = false;
  3.  
  4. // These direct where function calls from Javascript should go
  5. theBrowser.window.JSnewGeocodeData = JSnewGeocodeData;
  6. theBrowser.window.JSgeocodeError = JSgeocodeError;
  7. theBrowser.window.JSnewDirectionData = JSnewDirectionData;
  8. theBrowser.window.JSnewDirectionDataError = JSnewDirectionDataError;
  9.  
  10. // Example of calling a function on the htmlloader use .window.functionname
  11. theBrowser.window.calculateEntireRoute(tripArr);


In Javascript, after we have setup the callbacks in AIR/Flash we can call into the code by doing something like this:
  1. // Use window.functionname in Javascript
  2. window.JSnewDirectionDataError(_status);


The map with directions rendered.
If you run the examples and click the button in the lower right labled "Map Route", it will call a javascript function from inside of AIR that uses the Directions service to obtain, and map a route between my current town, Dewitt, Michigan and Fredericksburg, Texas with a stop at the Turkey Hill Experience in Columbia PA on the way.

When you click the "Map Route" button it in-turn calls the javascript function, calculateEntireRoute to preform a directions service call with three addresses. Once that returns in Javascript to the function handleAddressResults, it makes a call-back into Flash to the function JsnewDirectionData. From there in Flash you'll see I trace out all the directions, then pass the results object back to javascript; where the API finally renders the map with directions.

I've provided downloads of the entire example in CS4 Fla, Flash Builder 4.5, and FlashDevelop 4 which will hopefully satisfy the majority of you. They are all contained in the zip file.:

DOWNLOAD THE SOURCE FILES HERE


Best of luck on your migrations. If you have questions, please post them in the comments and I'll do my best to help you out.

Charles Palen has been involved in the technology sector for several years. His formal education focused on Enterprise Database Administration. He currently works as the principal software architect and manager at Transcending Digital where he can be hired for your next contract project. Charles is a full stack developer who has been on the front lines of small business and enterprise for over 10 years. Charles current expertise covers the areas of .NET, Java, PHP, Node.js, Javascript, HTML, and CSS. Charles created Technogumbo in 2008 as a way to share lessons learned while making original products.

Comments

Gecko
Gecko
February 20, 2014 12:05 am

Was so happy to find your post. Got it working for an iOS app project I have and then realized HTMLloader doesn't work on mobile... Back to zero. Your solution really works well on desktop though and I've learned interesting stuff about the HTMLloader class. Thanks !

MK
MK
November 18, 2013 2:56 pm

I got it working by making a html folder and placing the GoogleMapsV3_JSFunctions.html in there, and then adding to SRC. It's performance might even be better than AS3. Thanks very very much!!

Charles
Charles
November 18, 2013 2:29 pm

Hi MK,

If you place the html file inside the src directory it will be copied out with the package. If you create sub folders inside the src directory, files inside those will also be packaged.

Files and folders inside the src directory are relative to the application. So if you for instance has a src/html/htmlFile.html file.

The relative path to the file inside your application would be html/htmlFile.html.

MK
MK
November 18, 2013 12:36 am

Hi, thanks for the speedy reply! I do not actually see the html file included. And when I look at package settings/package contents there's not really a way to add it. How can I make sure that file gets added to the application directory? Thanks!

Charles
Charles
November 18, 2013 12:28 am

Hi MK.

When you package the AIR installer are you including the html file with it so the example has something to load?

Also, when you publish, Flash builder copies many of the files from src to another folder like bin-debug. Are you certain your paths for the html file aren't getting confused on export?

MK
MK
November 18, 2013 12:18 am

Hi, thanks very much for this!! It works perfect locally in FlashBuilder 4.7, but once I export a release build and create the .AIR package it does not show the map...only the map route button. Is there a security privilege I need to set or something? System.Security ???

Charles
Charles
September 10, 2013 3:44 pm

Hi Davide,

I just tried it again today and I'm getting the same thing you're talking about with street view. When I originally implemented this in 2011 it worked well.

Doing this in AIR might require unloading the html and re-loading it now to allow street view to work again once all those street view exceptions start happening.

I'm not sure about everyone else, but for like the last year, Google Maps throws a flash exception in my debug player when I load street view normally in a full browser...seems like someone has been slacking over there.

Davide Berra
Davide Berra
September 10, 2013 11:16 am

Hi Charles!

Did you encounter problem when switching to street view?

It seems like AIR got problems in rendering the "circle" following the mouse movements into Street View mode and a lot of javascript exceptions are generated.

Did you notice the same thing? Thank you

Charles
Charles
September 5, 2013 1:54 pm

Hi Zap,

The key component to this technique, the htmlLoader may not be supported on a mobile platform. This is directly from the Adobe documentation...but their documentation is out of date or blatantly wrong some times (especially when it comes to media server stuff).

"This feature is supported on all desktop operating systems, but is not supported on mobile devices or on AIR for TV devices. You can test for support at run time using the HTMLLoader.isSupported property".

If it does report supported then I would move the mapping of callback functions into the "htmlLoaded" event. By mapping of callback functions I mean:

theBrowser.window.JSnewGeocodeData = JSnewGeocodeData..etc.


I hope that helps.

Zap
Zap
September 4, 2013 7:59 pm

It's a runtime error

Zap
Zap
September 3, 2013 06:14 am

Here is the error i get:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Mapgoogle/createHtmlViewer()

Maybe i can't use the code inside mobile app?

Charles
Charles
September 2, 2013 4:32 pm

Hi Zap can you help me out a little and post the details of your error?

Is it compile time or a runtime error? The line you refer to is hooking up a loaded function in a HTML page to an action script function in your code.

Zap
Zap
September 2, 2013 06:47 am

Hi Charles,
i tried to use your code into a flash mobile project (with flash builder). I get one error at this line:

theBrowser.window.JSnewGeocodeData = JSnewGeocodeData
...and following lines.

Seems that javascript calls don't work.
Do i miss something?

Charles
Charles
April 26, 2013 5:19 pm

Hi Monteiro,

Look at the html file, "GoogleMapsV3_JSFunctions.html" there's a script reference right at the top and in big letters it says "YOUR_API_KEY". Replace that with ...well your api key.

Monteiro
Monteiro
April 26, 2013 12:49 am

I try to run on Flash Builder 4.6 with air 3.4:
But I get the following error:
Google has disabled use of the Maps API for this application.
The provided key is not a valid Google API Key.

Where do I change the api key?

Charles
Charles
January 30, 2013 09:09 am

dj_perry - The major downside here is that this example must be run inside of the Adobe AIR runtime. It will not render correctly inside of a webpage only likely due to security sandbox issues.

dj_perry
dj_perry
January 29, 2013 12:48 am

Hi, great job!
I've just a problem with the Flash version.
I've customed the .fla with the CS5 and the .html with Dreamweaver and if I open them through the .html page (GoogleMapsV3_JSFunctions.html) I see all the map and the markers/labels precisely.
But if I load the .swf as a movie inside a flash site (.swf) I don't see anything.
The curious thing is that if I do a preview inside flash CS5 I see the map but not the markers/labels.
The question is: how can I load the map inside a flash movie using the .swf file (and not the .html page)?
Thanks in advance!

Charles
Charles
November 21, 2012 10:51 am

Hi Jeff. The example on this page uses the Javascript API and javascript for all interactions with Google Maps. I am just displaying html pages inside of a browser window from inside an Adobe AIR application.

If you are using adobe AIR or something else in Flash that supports the htmlloader class, then you can use the same methodology. If you're doing this on a web site, then you're probably stuck with doing it all in html/javascript.

My use case was that I implemented a seamless maps solution in a full-screen Adobe AIR kiosk using the method I present here as a basis. So its an unusual use case, but it works.

Jeff
Jeff
November 21, 2012 00:28 am

hi Charles,

Did you mean a flash application still can work with JavaScript V3, (instead of Google flash API) properly?

Charles
Charles
November 17, 2012 2:36 pm

Hi Harold. The real bummer about this solution is that it will not work in web applications. The key htmlloader class is not available when running things without AIR. You may be able to use Flex, but I have not checked to see if the htmlloader class is available over there.

Harold
Harold
November 17, 2012 08:08 am

hi Charles,
first of al i am a beginner of AIR and as3. second sorry for my pour english.
when i opening de swf files is the map not visible, the button is flickering it looks like the swf file is in a kind of loop. this is also when ik publish de fla to the flashplayer. but when i publish this as a air file the map is visible en everything works fine. because i have a fuly flash site i would like to integrate this/your google maps app(load in my site as a extern swf file). do you know if ther is any way to do this, or what i must ajust to have a working swf file?

Shaun
Shaun
October 17, 2012 08:06 am

I came across a problem where sometimes the AbstractDataLoader would load, and other times it wouldn't make it to the HandleData function. Not sure why it would happen (network problem, my web server not wanting to serve the html page...?), but it would happen at least one in every four attempts.

I'm interested to hear any comments about why it was occurring in the first place, but my workaround that fixed it is this.

An an IO error listener...

AbstractDataLoader.addEventListener(IOErrorEvent.IO_ERROR, error_handle_if_no_load,false,0,true)

function error_handle_if_no_load(event:IOErrorEvent):void {
trace("ioErrorHandler: " + event)
AbstractDataLoader.load(new URLRequest("https://www.mysite.com/map.html"))

}

So essentially if the map page doesn't load the first time, it'll try again, and it seems to work. Of course, if there is a real problem like a connection break, it'll probably loop forever, so this still needs some more refining. But at least my map loads every time now.

FYI - the error code for my non-loading page is Error #2032: Stream Error

Shaun
Shaun
October 16, 2012 10:52 pm

Nevermind - it's a CS4 fla - saving and reloading it in CS5 format fixed the problem.

Shaun
Shaun
October 16, 2012 10:42 am

Gidday Charles

Excuse my ignorant question... I downloaded the source files, loaded the fla file into Flash CS5, hit ctrl enter to test the movie, but nothing happens. Can you please direct me as to what I need to do to see it in action? I'm obviously misunderstanding something fundamental here.

Thanks for your time and help.

Dennis
Dennis
September 28, 2012 2:57 pm

Hi Charles,

I think this is happening because the event Event.COMPLETE does not fire. I do not know exactly why, but as soon as is fire it creates a instance of window in theBrowser.
Thanks!

Dennis.

Charles
Charles
September 27, 2012 4:23 pm

Hi Dennis.

I have a hypothesis about your problem. This example loads a html file externally for the content of the htmlLoader, in this case named theBrowser.

There is a time delay to load that html into the application and render it in the browser before you can call javascript inside of it.

The first key thing is to ensure you wait for the HandleData function to finish. The second thing that I dont recall if I included in this example is to ensure the html and DOM are initialized in the htmlLoader control. If you look at the reference http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/html/HTMLLoader.html

You may have to listen for the htmlDOMInitialize or htmlRender events as well before you are safe to interact with the javascript.

Dennis
Dennis
September 27, 2012 3:05 pm

Hi Charles,

I'm just getting started with flash/flex/air programming language and I find your example a interesting alternative to Google Maps API for Flash.
Well.. I'm trying to use your example, but when AIR tries to link Javascript functions (ex: theBrowser.window.JSnewGeocodeData = JSnewGeocodeData) it says window is null and raises and exception: Cannot access a property or method of a null object reference.
Do you have any idea of what it could be?

Thanks in advance!
Dennis.

Charles
Charles
August 20, 2012 08:12 am

Miqua,

This solution hinges on the HTMLLoader class to show a browser within an Adobe AIR project. Check if whatever your build environment is, has access to HTMLLoader. I don't think yours does.

If your project is a web project, why not just use all Javascript? If you need to communicate with Javascript on the same page as a Flash application, you could check out ExernalInterface. Here is an example.

Miqua
Miqua
August 16, 2012 10:02 am

Hi Charles,
Thanks for sharing this good rare work.
As I don't use AIR but flashbuilder-php, do you think possible to do the same integration of maps JS API V3 ?
I looked on the Internet for an example of migration from flex maps API to JS maps API V3 without success !
Thanks in advance and Merci beaucoup for the nice Job !!!
Miqua

sid
sid
June 26, 2012 03:21 am

I have a 1 applicaton in that we can store and manages our contacts,mails etc. and i want add 1 additionl functionlly of exporting my addressbook contact to microsoft outlook mail and i want dis funtion on submition of button click is it possible thorugh coding in classic asp if there is a such a way plz let mi kw

Charles
Charles
June 2, 2012 12:32 am

Teckel,

If you are not running the application as AIR, there are more security restrictions. The error you describe doesnt sound like a sandbox error.

If I were you, I'd start with the html portion and see if you can manipulate the API soley using html and javascript. Then if you can get that solid, you can attempt to execute the JS calls from AIR.

My actual deployment of this final solution is operational as a full-screen AIR application in a museum kiosk.

Teckel
Teckel
June 2, 2012 07:24 am

Hi, interesting post. But i missing something.
But how you avoid the sandbox error that triggered because the html load a script on the google domain?
I try to do the same but console say : 'ReferenceError: Can't find variable: google'

Erika
Erika
May 29, 2012 10:50 pm

Thanks Charles, I figured out that I just need to delete the part that mention the key and it runs well, very useful post.

Charles
Charles
May 15, 2012 1:44 pm

Erika,

Glad to hear it looks useful.

Did you download the zip file from this page? The gm_JavascriptAPI_CS4.swc is being generated from the project in the directory labeled FlashCS4. It simply is providing the button to the other projects, there is no code in the swc that interacts with the Google Maps API.

Are you running the Flash Develop or Flash Builder version of the example? Maybe I can point you in the right direction if I know.

Erika
Erika
May 15, 2012 11:52 am

Can you provide the project where you generated gm_JavascriptAPI_CS4.swc the google api version 3 doesnt need a key and it is showing and error.
Thanks, your code looks very useful

Bruce
Bruce
December 5, 2011 4:05 pm

Pretty cool. Glad to see there is still a way of using the Google Maps API in AIR applications. The mapping is so robust, it would be a shame to lose that as a resource to AIR for the many mapping applications that museums and visitor centers need.

Comments are currently disabled.