Google Maps in OpenLaszlo

Recently, I wrote about Rmenuz, a site that embedded Google’s Flash maps in OpenLaszlo. The Google Flash Maps are written in ActionScript 3 (AS3), which requires Flash Player 9 or above. That means you wouldn’t be able to use them in OpenLaszlo 4.1 or below. I figured that with OpenLaszlo 4.2b3, which supports compiling to SWF9, it should be possible to embed an AS3 component. With some much-appreciated help from Henry, I got the following application working:

I wanted an example that:

  • Embedded Google maps in a clean, developer-friendly way.
  • Was full-featured (allowed a user to search for an address, and add a marker there).
  • Passed information (such as where to add a marker) from the OpenLaszlo application to the map component.
  • Passed information (such as the address of the marker that a user clicked) from the map to an object in the OpenLaszlo namespace.

Note that the Google Maps geocoding service is Flash-specific. Even though it passes XML back to the client, you need to use Google's Flash-based APIs to call it. I didn't want to do this; I wanted my example to perform the search in OpenLaszlo, and pass instructions to the map component. So I used Yahoo Maps' excellent geocoding service.

Here are the instructions on how to do this:

  1. Download OpenLaszlo 4.2b3 or later, and install.
  2. Get a Google Maps API key (free).
  3. Download the Google Maps API for Flash SDK, from Google's web site. I used version 1.7.
  4. Find the WEB-INF directory in your OpenLaszlo install, and create a directory called flexlib inside that. i.e. WEB-INF/flexlib/
  5. Unzip the Google Maps SDK, and locate the two SWC files in the lib directory. Take the Flash one (mine was called map_1_7a.swc) and copy it into WEB-INF/flexlib/.
  6. In your OpenLaszlo my-apps folder, create your lzx file (e.g. main.lzx), and write your application.
  7. When you compile the app via the browser, make sure that you remember to compile to SWF9. When you request the LZX file, append ?lzr=swf9 to the request. (e.g. main.lzx?lzr=swf9).

Here's my complete source:

<canvas proxied="false" width="468" height="530">

    <!-- antunkarlovac.com key -->
    <attribute name="gmapsKey"
               value="ABQIAAAAN0JyO4tW04-1OKNW7bg9gxSPySWqAfkZkuZG2U8jr6yyIuV3XBSrEn410_O9d9QPJh3dbWV85Qad8w" type="string" />

    <alert name="errorDialog" width="400" />

    <method name="handleMarkerClick" args="address">
        addressWin.writeAddress(address);
    </method>

    <script when="immediate"><!&#91;CDATA&#91;
        class FlashMapOL {
            #passthrough (toplevel: true) {
                import com.google.maps.*;
                import com.google.maps.controls.*;
                import com.google.maps.overlays.*;


                import flash.geom.*;
            }#

            var map:Map;

            function createMap() {
                map = new Map();
                map.addEventListener(MapEvent.MAP_READY, onMapReady);
                map.key = canvas.gmapsKey;
                map.setSize(new Point(450, 300));
                return map;
            }

            function onMapReady(event:MapEvent):void {
                map.addControl(new ZoomControl());
                map.addControl(new PositionControl());
                map.addControl(new MapTypeControl());
            }

            function addMarker(lat:Number, lon:Number, address:String):void {
                map.clearOverlays();
                var latLon:LatLng = new LatLng(lat, lon);
                var marker:Marker = new Marker(latLon);
                marker.addEventListener(MapMouseEvent.CLICK, function(e:MapMouseEvent):void {
                    canvas.handleMarkerClick(address);
                    map.openInfoWindow(latLon, new InfoWindowOptions({titleHTML: "<b>Search Result</b>", contentHTML: address}));
                });
                map.addOverlay(marker);
            }

            function centerAndZoom(lat:Number, lon:Number):void {
                var latLon:LatLng = new LatLng(lat, lon);
                this.map.setCenter(latLon, 14, MapType.NORMAL_MAP_TYPE);
            }

        }

        lz.mapFactory = new FlashMapOL();
        
        lz.map = lz.mapFactory.createMap();
    &#93;&#93;>
    </script>

    <dataset name="geoCode_ds"
             src="http://local.yahooapis.com/MapsService/V1/geocode?" type="http" request="false"/>

    <datapointer name="result_dp" 
                 xpath="geoCode_ds:/ResultSet/Result&#91;1&#93;" />

    <handler name="ondata" reference="geoCode_ds"><!&#91;CDATA&#91;
        var lat;
        var lon;
        var root_dp = geoCode_ds.getPointer();
        root_dp.selectChild();
        if (root_dp.getNodeName() != "Error") {
            lat = result_dp.xpathQuery("Latitude/text()") * 1;
            lon = result_dp.xpathQuery("Longitude/text()") * 1;
            var warn = result_dp.getNodeAttribute("warning");
            if (warn != undefined) {
                // Pass along any warnings (e.g. couldn't find address,
                // centering on city.
                errorDialog.setAttribute("text", warn);
                errorDialog.open();
            }
            // Get full address as a string
            var address = result_dp.xpathQuery("Address/text()")
                        + ",<br />" + result_dp.xpathQuery("City/text()")
                        + ", " + result_dp.xpathQuery("State/text()")
                        + " " + result_dp.xpathQuery("Zip/text()")
                        + "<br />" + result_dp.xpathQuery("Country/text()");
            lz.mapFactory.centerAndZoom(lat,lon);
            lz.mapFactory.addMarker(lat, lon, address);
        } else {
            // Failed to find address
            var msg = root_dp.xpathQuery("Message/text()");
            errorDialog.setAttribute("text", msg);
            errorDialog.open();
        }
    &#93;&#93;>
    </handler>

    <window name="mapWin" title="Map" width="468" height="369" 
            allowdrag="false">
        <passthrough>
            import flash.display.*;
        </passthrough>
        <handler name="oninit">
            var sprite:Sprite = this.content.sprite;
            sprite.addChildAt(lz.map, sprite.numChildren);
        </handler>

        <view name="buttons" bgcolor="0xeaeaea" width="100%"
              valign="bottom">
            <simplelayout axis="x" spacing="10" />
            <text valign="middle">Address Search:</text>
            <edittext name="addressInput" width="260" 
                      text="1221 Mariposa Street, San Francisco, CA" />
            <button>Search
                <handler name="onclick"><!&#91;CDATA&#91;
                    var qs = "appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--" 
                           + "&amp;amp;location=" + parent.addressInput.text 
                    geoCode_ds.setQueryString(qs);
                    geoCode_ds.doRequest();
                &#93;&#93;>
                </handler>
            </button>
        </view>
        
    </window>

    <window name="addressWin" title="Address" width="468" height="150" 
            y="379" allowdrag="false">
        <method name="writeAddress" args="address">
            this.txt.setAttribute("text", address);
        </method>

        <simplelayout axis="y" spacing="10" />

        <text width="100%" multiline="true" fontstyle="italic">
            Click the search button to search for an address. When you click
            one of the markers, the full address will be displayed in the
            OpenLaszlo text field below:
        </text>

        <!-- Address display area -->
        <text name="txt" width="100%" multiline="true" fontsize="14">
        </text>
    </window>

</canvas>

Note a few important points in the code:

  • There's AS3 code directly inside the script block in the canvas. You need to use the when="immediate" option in the script tag for this.
  • There's an OpenLaszlo compiler instruction that allows you to include libraries from the WEB-INF/flexlib/ directory:
    #passthrough (toplevel: true) {
    ...
    }#
  • There's another OpenLaszlo compiler instruction in the view where you want to instantiate the map (there's a new passthrough tag, and the map must be added procedurally - sprite.addChildAt().

Here's the complete source to my app, as a zip.

31 thoughts on “Google Maps in OpenLaszlo

  1. I’m just curious why the “immediate” qualifier needs to be used by the script tag. I know it ensures the script tag is executed earlier, but why does the script code need to execute earlier? It already executes very early within the execution cycle. What does it need to precede?

  2. Fantastic stuff. This gets around the drawbacks of using the html laszlo tag (iFrame solution). Of course it does indeed tie it very much down to a flash runtime, but for us that’s of little consequence.

    Is there any way to get Webtop to use versions of Laszlo other than what it ships with? It’d be great to be able to use this solution in a Webtop app, but that doesn’t use a new enough version of OL.

  3. Hey Simon,

    Unfortunately there isn’t a way to use a different version of OpenLaszlo other than the one that comes bundled with Webtop. The Webtop client code is all precompiled into .lzo files; it ships with the Webtop distribution.

    There are code changes that would need to be made to the Webtop client code (by Laszlo) to move it from where it is today – 4.0.x – to 4.2. I wrote about moving an OpenLaszlo application from 4.0.x to 4.1 a while back (http://www.antunkarlovac.com/blog/2008/07/02/migrating-lzx-code-to-openlaszlo-41/ ). There’s also a script that comes with the 4.1.1 distribution that does the migration for you. As a result, it’ll be a little while before Webtop is running on OpenLaszlo 4.2, but given the attractive performance gains of SWF9, I hope the project will get prioritized.

    -Antun

  4. Hi Carsten,

    There may be a problem with the geocoding service. It’s working for me if I use the app on this page. Can you see what is being returned? To do so:

    1. Enable the Debugger (add debug=”true” to the canvas), and recompile the application.
    2. Search for an address.
    3. Enter geoCode_ds.serialize() in the Debugger, and hit the “Eval” button.

    Thanks,

    Antun

  5. Hi Antun.

    I’ve been working lately in a project (in fact my site upgrade) and choose Laszlo and Google maps for this, although the dhtml way. I’ve managed to develop a fairly functional component, but I’m stuck in some basic mouse functionality (Chrome & Safari become unresponsive to dragging, and some clicking; Firefox, IE and SeaMonkey work well) and I could use some help 🙂 that is, if you are interested (BTW, when ready, I’ll release it as Open Source).

    If you wan’t to see it in action, I have it working at http://asuka.depatadeperro.com/ Just be aware that this is my test site, so it might now behave ok every time.

    Oscar

  6. The following is the problem that we are facing in our application Order Entry Application:

    This is developed using OpenLaszlo. In our application we need to include google maps.

    We found that the site below is havingsteps to include Google maps in openlaszlo:
    http://www.antunkarlovac.com/blog/2008/11/17/google-maps-in-openlaszlo/

    This uses Google Maps API for Flash SDK version 1.7 but the available download version is 1.8(http://maps.googleapis.com/maps/flash/release/sdk.zip). With this version 1.8 the sample code is not displaying the maps. It’s not throwing any error also. Please let us know which part of the coding we have to change related to Google Maps API for Flash SDK version 1.8 in the above sample code.

    Running http://localhost:8080/lps-4.2.0/my-apps/main.lzx?lzr=swf9 doesn’t show up a map.

    If we Enter geoCode_ds.serialize() in the Debugger, and hit the “Eval” button. It’s returning the value . Please let us know how to get google maps working.

  7. Hi Ajit,

    I just downloaded the Google Maps API for Flash SDK 1.8 and tried the steps listed in my blog post above. I used OpenLaszlo 4.2.0. The application ran successfully. I was able to see the maps, and then search for a location.

    Are you sure that you are following the exact steps listed in my post (i.e. steps 1-7)?

    -Antun

  8. Hi Antun,
    Like Ajit, I too am unable to retrieve a map.
    I’m using 4.2.0 with 1.8 sdk.
    I’ve copied your code and replaced the API key with my local API key.

    In testing, I decided to check if maybe the API key was wrong, so I removed half of the API key in an attempt to get an error of the wrong API key, and I don’t get any such response.
    So my guess is that the API key s not being retrieved for some reason.

    I have very little experience with OpenLaszlo having only written one simple page with it before, so I’m not really sure how to get an API Key response error.

  9. If you’re having trouble with the maps, there are a few approaches to debugging:

    – Make sure that the OpenLaszlo debugger is enabled. (i.e. debug=”true” in the canvas tag). See if there are any errors there.
    – Download and install the Flash debug player. It’ll automatically throw a dialog box with any ActionScript 3 exceptions it catches.
    – Examine the HTTP traffic (using a tool like Firebug, Fiddler, Charles, etc.), to see what’s being returned.

    Feel free to post any output here.

    By the way, are you running this from localhost, or from a remote server that you’re accessing by domain name/IP address?

  10. Thanks Antun,

    I am trying to run this from localhost (actually the OpenLaszlo default 127.0.0.1:8080/…).
    I followed your advice (I had already been running the debug=true). The http traffic returns without errors, but the Flash Debugger returns the following
    Error: No class registered for interface ‘mx.styles::IStyleManager2’.
    at mx.core::Singleton$/getInstance()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/core/Singleton.as:111]
    at mx.styles::StyleManager$/get impl()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/styles/StyleManager.as:78]
    at mx.styles::StyleManager$/get http://www.adobe.com/2006/flex/mx/internal::stylesRoot()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/styles/StyleManager.as:100]
    at mx.core::UIComponent/http://www.adobe.com/2006/flex/mx/internal::initProtoChain()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/core/UIComponent.as:7480]
    at mx.core::UIComponent/regenerateStyleCache()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/core/UIComponent.as:7690]
    at mx.core::UIComponent/http://www.adobe.com/2006/flex/mx/internal::addingChild()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/core/UIComponent.as:5199]
    at mx.core::UIComponent/addChild()[/Users/hqm/openlaszlo/adobe/trunk/frameworks/projects/framework/src/mx/core/UIComponent.as:4916]
    at com.google.maps.wrappers::SpriteFactory/addChild()
    at com.google.maps.wrappers::ISpriteFactoryWrapper/addChild()
    at com.google.maps.core::Bootstrap$/createChildComponent()

    I’ve been searching for a solution to the mx.styles issue (as I’m somewhat assuming that will resolve everything else) and came across a few posts between you and Henry, but nothing with a resolution.
    I have both of the flex and flash file in the flexlib folder (though I tried removing the flex file to see if that would help, it didn’t).

    Thanks for your help,
    Pete

  11. Hi Antun,

    Thanks for response. I followed the every steps mentioned. As far as the Google Map API key, I tried with your key and also generate a key. Result is same. I can’t see the map. I generate the key by giving arbitrary site name. Do you think it is the problem?

    For testing purpose I deleted the flexlib folder inside the WEB-INF, that I created and compile the application again, but there is no compilation errors or warnings but no map aslo.

    Do I need any particular version of flash or any supporting files?

    -Ajit

  12. I have a laszlo-google app that uses a laszlo InfoWindow…all works okay except for the scroll bars and select list controls.

    Any ideas? Thanks…

    The site is http://www.rmenuz.net/UW_PROJECT_A

    Search on Trees -> Tree number -> 1

    Next, open the google InfoWindow and select “Proximity Search” tab.. Draw a 200 foot proximity and select “Trees” again…notice no scroll bars and only check boxes.

  13. Hi autun,
    I tried to make my app step by step as your instruction. but there’s nothing in my map place.. hix.. I used my Google key..
    Then, I download your file, use my Google key, but there’s the same, nothing in the window, it’s blank.. could you tell me how to run it successfully?

  14. I use laszlo quite a bit to create complex input forms (as everyone knnows building any form in HTML is an adventure), but why would one need to create a laszlo based google map and introduce all that overhead when making ajax-enabled maps is quite easy? Just a thought. Great solution though!

  15. I’m using OpenLaszlo v 4.3.0 and I’m compiling to Flash 9 or 10. I have map_1_9a.swc in the ..\Server\lps-4.3.0\WEB-INF\flexlib folder.

    The map displays, the search works and the 3 controls are displayed (Zoom, Position and MapType). The buttons (arrows, +, -, etc) on the controls do not work. I can double click on the map itself to zoom in. The little bar on the zoom control can be grabbed used to zoom in an out.

    It appears that the click events are not being handled.

    You can see the App at http://www.himweb.org/openlaszlo

    This my experiment page for OL Apps. The debugger does not display any error messages or warnings.

    Thank you,

    Richard

  16. Richard,
    Did you try the example that ships in the incubator
    (lps/components/incubator/googlemap.lzx) , run in swf9 runtime (lzr=swf9)

    The various control buttons do respond to mouse clicks. Is it possible your window is intercepting mouse clicks with some other onclick handler?

  17. did anyone see the same problem as richard and fix it? i have 4.2 with 1.9 and see exactly the same.

    thanks,
    frank

  18. some more info on the zoom/position/type controls not reacting: i was using map_1.9a.swc. using map_1.9.swc it does work fine!

  19. Henry and Antun:

    Why this ?lzr=swf9 is making the doRequest() method not returning the resultset?

    It is calling backend api but no resultset with swf9 and it is working fine with swf8!

    Any ideas or solutions? is this bug in lzx4.2 and lzx 4.4?

    Thanks,

    Charlie

  20. Hi Charlie,

    Can you add the following somewhere in the canvas tag to see what is being returned by the web service in both SWF8 and SWF9:

    <handler name="ondata" reference="geoCode_ds">
    Debug.write(geoCode_ds.serialize());
    </handler>

  21. Hi Antun,

    It is not just for the map, all other DataSet that I am using has same problem that returns null when I use ?lzr=swf9 but all things are find with ?lzr=swf8.

    Anyway, I followed your instructions and it is not coming to ondata but onerror.

    so onerror I did serialize it and it is showing me just called DataSet name without and contents.

    To prove, ?lzr=swf8 is working, I did serialize it and shows all data what supposed to be.

    In my opinion, there is something that I am do not know about usage of ?lzr=swf9 or problem with swf9 or that is related with flex configuration that I found this message from logs about how flex is used when I do ?lzr=swf9.

    here is the message that hopefully you can get hints,

    Executing compiler: (cd /Applications/apache-tomcat-5.5.26_parent/temp/lzswf9/lzgen60827; /Applications/apache-tomcat-5.5.26_parent/webapps/splash/WEB-INF/bin/mxmlc -load-config=/Applications/apache-tomcat-5.5.26_parent/webapps/splash/WEB-INF/frameworks/flex-config.xml -compiler.show-actionscript-warnings=false -compiler.source-path+=/Applications/apache-tomcat-5.5.26_parent/temp/lzswf9/lzgen60827 -debug=true -compiler.headless-server=true -compiler.fonts.advanced-anti-aliasing=true -output /Applications/apache-tomcat-5.5.26_parent/temp/lzswf9/lzgen60827/app.swf -default-size 500 400 -compiler.library-path+=/Applications/apache-tomcat-5.5.26_parent/webapps/splash/lps/includes/lfc/LFC9-debug.swc -compiler.library-path+=/Applications/apache-tomcat-5.5.26_parent/webapps/splash/WEB-INF/flexlib -frame two LzSpriteApplication -file-specs=/Applications/apache-tomcat-5.5.26_parent/temp/lzswf9/lzgen60827/LzPreloader.as)
    compiler output is Loading configuration file /Applications/apache-tomcat-5.5.26_parent/webapps/splash/WEB-INF/frameworks/flex-config.xml
    /Applications/apache-tomcat-5.5.26_parent/temp/lzswf9/lzgen60827/app.swf (307798 bytes)

    compiler output:
    Loading configuration file /Applications/apache-tomcat-5.5.26_parent/webapps/splash/WEB-INF/frameworks/flex-config.xml
    /Applications/apache-tomcat-5.5.26_parent/temp/lzswf9/lzgen60827/app.swf (307798 bytes)

    Cheers,

    Charlie

  22. I found the problem and solved it that was matter of welformed XML issue that ?lzr=swf8 is not checking it but ?lzr=swf9 is sensitive for this so that it looks like No Resultset is back to caller.

    Hopefully this information save somebody’s time as I spent 2 days to find this.

    Thanks,

  23. Thanks for posting that Charlie! I guess that the underlying Flash APIs must do better XML validation in SWF9 than they did in SWF8.

    Was there any error at all – in the Debugger? If not how did you discover the problem? The reason I ask is that if there was no Debugger error, it may be possible to add one.

    -Antun

  24. Hi, How to compile my laszlo as swf9?Just exchange ‘lzr=swf8’ to ‘lzr=swf9’ ?and that’s OK? If I want to compile my laszlo into swf with solo in laszlo server, What could i do?

    Thanks a lot.

  25. Leo,

    You should simply be able to compile your app to SWF9 by adding ?lzr=swf9 to the querystring, if you’re using Tomcat. If you’re using the command line compiler, you’d need to add a –runtime=swf9 argument.

    -Antun

  26. People who run the Antum code and see the boxs but don´t see the maps and can´t make a searching, check if you have added only the “map_1_7a.swc” file into “WEB-INF/flexlib” folder or if you have added the file “map_flex_1_7a.swc” too. If you have added this two files clear “map_flex_1_7a.swc” from “WEB-INF/flexlib/”folder and it works perfectly.

    Excuse me for my english and thanks Antun for the tutorial!

Leave a Reply to antun Cancel reply

Your email address will not be published. Required fields are marked *