Google Maps JavaScript API and Flex-IFrame

Introduction
Implementing Google Maps for Flex application proved to be more challenging than it looked at first. Requirement was to have Google Maps implementation as much as possible like maps.google.com. This ruled out Google Maps Flash API (http://code.google.com/apis/maps/documentation/flash/). It has no support for Street View (http://code.google.com/p/gmaps-api-issues/issues/detail?id=524) and look and feel is much different than of maps.google.com. At this point it was clear that we’ll have to use Google Maps JavaScript API.

Implementing first demos of HTML overlay without using any additional libraries went well. Maps were  flowing above our Flex application quite well. Proving this was an acceptable approach, we decided to use the Google Maps JavaScript API together with Flex-IFrame (http://code.google.com/p/flex-iframe/) library to make the implementation more clear and to avoid duplicate work. To help us with some common JavaScript tasks we also used a bit of jQuery.

Flex-IFrame
Flex-IFrame is quite robust library that lets you overlay HTML content over Flex application. It’s easy to use, you just need to define an IFrame MXML tag with source or content properties defined. Here is an example with source defined:

<flexiframe:IFrame id="_mapIFrame" visible="false" source="GoogleMaps.html" width="798" height="449" frameLoad="onMapIFrameLoad(event)"/>

Flex-IFrame will tell HTML wrapper to setup the floating div on top of your application and create an iframe inside of that floating div. Flex-IFrame will position the HTML overlay according to the position of the Flex-IFrame component in your MXML. Communication with the source HTML document is also setup automatically by the library. There are many more features in the library. Download Flex-IFrame archive (http://code.google.com/p/flex-iframe/downloads/list) and check out the code, asdocs and examples in it.

Before we continue it’s really important that you read the FAQ section of the Flex-IFrame project here (http://code.google.com/p/flex-iframe/wiki/FAQ). Be sure that you know or learn as much as possible about wmode and related issues or it might bite you in the end. Check out these articles to get better understanding about how different wmode values behave:

Our implementation suffered from performance issues in IE. Check out a snippet about it here: http://snippet.gnstudio.com/viewtopic/97/Performance_boost…. You’ll need to do a lot of cross browser testing to ensure that you don’t have any unwanted side effects from changing the wmode parameter. Our decision was to set wmode to “opaque” only for IE (for all other browsers we use the default wmode, “window”.), since “opaque” wmode caused issues with mouse wheel scrolling in Chrome and Mozilla Firefox.

If you are still with us it means that you are in the same position as we were and you just don’t have any other options but to go with the iframe approach. Here are some modifications we made to Flex-IFrame library that helped us developing the application (changes were done on Flex-IFrame 1.4.6).

  • First change was removing the “_iframeContentHost != _appHost” condition. The way the _iframeContentHost and _appHost are originally resolved prevented us to use relative paths for source. There were also problems testing our application locally. The iframe just wouldn’t show. These host variables are concatenated from protocol and server name with port string taken from source and application URL. You will have to remove “_iframeContentHost != _appHost” condition from IFrame class on several places to avoid problems above.
  • One addition was also made. We added public getter for _frameLoaded flag to IFrame class to access this information more easily.
  • Another change that we made was related to the issue #69 from Flex-IFrame issue tracker (http://code.google.com/p/flex-iframe/issues/detail?id=69). By default Flex-IFrame expects  you to declare  methods like this “document.myFunction = function () {};”. This means that if you declare your methods like “function clickMe() {}”, Flex-IFrame won’t be able to find them and the call will fail. We made a change proposed here in the comments http://code.google.com/p/flex-iframe/issues/detail?id=69#c7.

If you’re having some problems using Flex-IFrame, check out project’s issue tracker. Maybe you’ll find your answer there (http://code.google.com/p/flex-iframe/issues/list).

Google Maps JavaScript API
Working with Google Maps JavaScript API is quite straightforward. There is documentation and a lot of examples out there.
There was a requirement to customize the look of the callout when users click on a marker. We used the InfoBox (http://googlegeodevelopers.blogspot.com/2010/04/infobox-10-highly-customizable.html), now part of the google-maps-utility-library-v3 (http://code.google.com/p/google-maps-utility-library-v3/), to accomplish requested visual design.

The InfoBox worked ok for us, but we had problems closing it with single left mouse click outside a callout when in Street View. Street View doesn’t dispatch mouse click events, so we had to implement our solution to get around this. It would be much easier to just have mouse click events on the Street View.
Traffic layer had to be show in all combinations, just like at maps.google.com. We had problems showing satellite view with no labels, but with traffic. This example from documentation shows us how to do it by defining custom map overlay: http://code.google.com/apis/maps/documentation/javascript/maptypes.html#ImageMapTypes. With some additional JavaScript coding we made it work just like at maps.google.com.

Note that we were not able to mimic all things from maps.google.com. There is no way to show transit routes like in Google’s official application. Also Street View implementation is different since maps.google.com uses Flash version of the Street View and there is no option in API to include it via API. Google wont fix this also. Here is the explanation why: http://code.google.com/p/gmaps-api-issues/issues/detail?id=2999. Our guess is we’ll see HTML5 version at maps.google.com soon, so there shouldn’t be any differences in the future.

Don’t forget to freeze your version of Google Maps JavaScript API for production. There is a “v” parameter that can be set to a specific version number when importing the library (http://code.google.com/apis/maps/documentation/javascript/basics.html#Versioning).

If you don’t have Google Maps API Premier account and you are serving your application over HTTPS, IE security warning pop-up can be problematic. We had to prove our implementation works before acquiring Google Maps API Premier account. We had problems with the code that depends on Google Maps API because IE blocked Google Maps API from loading. Code that required Google Maps API was executed even if API was not loaded. To solve this we used “callback” parameter (example: http://maps.google.com/maps/api/js?v=3.4&sensor=false&callback=initialize). This is the only way you can be sure Google Maps API is ready to use. Callback function enables us to wait for user to accept unsecure content. When we are sure that the API is ready, we start to load other libraries in a sequence (like InfoBox that needs Google Maps API to initialize properly). After all libraries are loaded we start to initialize the map. This way we ensured that our implementation works even if browser prompts user about unsecure content.

Integration
Our use case was to open the map when user clicks on a button. Map has to be in a Flex pop-up which is centered as user resizes the application. Positioning and centering worked well from the start, but Flex-IFrame was flickering when the pop-up was opened. It appeared in the top left corner of the application and then it would quickly center itself. This was of course unacceptable, so we had to find a solution for it.

Solution that worked best for us is not elegant at all. We added listener for Flex-IFrame frameLoad event and made sure that Flex-IFrame was hidden when we opened our Flex pop-up. When frameLoad listener was triggered , timer is setup to wait 100 ms for Flex-IFrame to center itself and for Google Maps to initialize. After 100 ms we set the Flex-IFrame visibility to true and we kick off map initialization via external call. Hundred milliseconds worked best for us. It’s not to much for user to wait and the flicker was gone.

Except for issue above the rest of the integration went well. It was a long journey from PoC to a final polished implementation.

Conclusion
This experience points out again that Flash needs to mashup with other technologies more easily. We can’t expect that all popular applications, widgets and APIs will be written, adopted and maintained for Flash platform also. It would really be good to have more support for mashing Flash content with other web content. Not sure if iframe is a solution for Flash and web mashups, but you can vote here to include iframe component to Flex SDK (http://bugs.adobe.com/jira/browse/SDK-12291). We can hope for two things, either Flash becomes more open web technology or we get all popular APIs written and maintained for Flash platform.

We didn’t expect so much trouble implementing Google Maps for our Flex application. The fact that we had to mimic maps.google.com made it more challenging. We had to ignore the Google Maps Flash API and go for JavaScript API because it’s more up to date. We had to use iframe technique and would recommend it only as last resort. You may spend a lot more time than planned for the implementation. Hope that this article will help you avoid some of the issues we had and speed  up you implementing Google Maps for your Flex application.

This entry was posted in Best Practices, flex, general. Bookmark the permalink.

5 Responses to Google Maps JavaScript API and Flex-IFrame

  1. Douglas says:

    Did you run across any issues with scrollbars?
    In IFrame.as they have added constants to handle scroll policy (issue 50 http://code.google.com/p/flex-iframe/issues/detail?id=50):
    /**
    * Value for the ‘auto’ scroll policy.
    */
    public static const SCROLL_POLICY_AUTO : String = “auto”;

    /**
    * Value for the ‘on’ scroll policy.
    */
    public static const SCROLL_POLICY_ON : String = “yes”;

    /**
    * Value for the ‘off’ scroll policy.
    */
    public static const SCROLL_POLICY_OFF : String = “no”;

    /**
    * The scrolling policy applied to the iframe.
    */
    public var scrollPolicy : String = SCROLL_POLICY_AUTO;

    Of which is then passed out through externalInterface:
    protected function loadIFrame():void
    {
    logger.info(“Loading IFrame with id ‘{0}’, on SWF embed object with id ‘{1}’.”, _frameId, applicationId);
    ExternalInterface.call(IFrameExternalCalls.FUNCTION_LOADIFRAME, _frameId, _iframeId, source, applicationId, scrollPolicy);
    }

    IFrameExternalCalls.as FUNCTION_LOADIFRAME, around line 220 takes the scrollpolicy parameter.

    I have been unable change scrollbar behavior regardless of constant assignment – or even hard-coding public var scrollPolicy = SCROLL_POLICY_OFF; (or ‘no’ for that matter).

  2. strasnihogar says:

    Hey Douglas,

    We didn’t have any issues with scroll policy since Google Maps always fitted size of the container.

    We tested patch you provided for the issue #50 (http://code.google.com/p/flex-iframe/issues/detail?id=50#c6) and it works for us also. Nice! 🙂

  3. Douglas says:

    @strasnihogar Thanks for testing the patch!

  4. liaratsoni says:

    Do you have an example working? (:
    Thanks

    • gnstudio says:

      Actually we worked on a real application a lot of time ago and the APP is now in the intranet of the customer so I’m sorry but we cannot show it to you…

Leave a reply to gnstudio Cancel reply