Support & Tutorials  »  Google Maps App Tutorial

Tutorial: Google Maps App

Download the sample application
Download Download the sample application

Introduction

Learn how to dynamically drag, rotate, scale, and tilt a Google Map to create a multi-gesture application. In this tutorial, we’ll pull in Google Maps data via its API, then use Touch Drag, Gesture Rotate and Gesture Scale, and 3-D Tilt Gesture events to create a Google Map that is fully multitouch-controllable. This tutorial requires Adobe Flash CS4 and GestureWorks (download a free trial).
 

Creating Your Document Class

The document class, also known as the “Application” class, is the root object class for your application. The primary purpose of the document class is to extend the main timeline; it does this in the form of a custom MovieClip. This means that all other objects in your application are placed within it.

To set the document class for your application, open mainApp.fla. Then, in the main menu, go to File > Publish Settings. In the Flash tab, select the Script “settings” button. Then, in the “Advanced Actionscript 3 Settings” window, set the “Document Class” to “Main“. Press the “OK” button and apply the changes to publishing settings.

In the main menu, go to: File > New. Then, in the “New Document” window, select “Actionscript File” and press the “OK” button. Save the file as “Main.as” and make sure it is saved in the same folder as your application file.

In the first line of your ActionScript file, add the following code to open a new package structure:

1
package {

This defines the beginning of the root package, which defines the code used in your root class “Main” and tells Flash what script to include when extending “Main”.

Importing Flash Libraries

In this example, we will be using the point object and modifying the mouse behavior. To access these classes, we need to import the required Flash libraries.

2
3
  import flash.geom.*;
  import flash.ui.Mouse;

Importing GestureWorks Libraries

Importing the “Application” package instantiates libraries and builds a framework of control structures for managing multitouch events and methods for object tracking within Flash. To edit these settings, open “How to Customize Application.xml”

5
  import id.core.Application;

A display level class must be set up to be used for stage objects. To do this, we import the TouchSprite class libraries. TouchSprite builds multitouch onto the Sprite class. This means that TouchSprite inherits all the properties of Sprite and adds new event targeting features that act systematically to determine multitouch event ownership at the object level.

6
  import id.core.TouchSprite;

Since we are going to be using touch events and gesture events, we’ll need to import these classes into our file.

7
  import gl.events.TouchEvent;

As with importing other packages, importing the TouchEvent and GestureEvent packages provides a reference to all available touch and gesture analysis modules via their class names.

8
  import gl.events.GestureEvent;

Importing Google Maps Libraries

In order to access the built-in classes in the Google Maps API, we need to import all required google.maps libraries.

10
11
12
13
14
15
16
  import com.google.maps.LatLng;
  import com.google.maps.Map3D;
  import com.google.maps.MapEvent;
  import com.google.maps.MapOptions;
  import com.google.maps.MapType;
  import com.google.maps.View;
  import com.google.maps.geom.Attitude;

Creating Your App

In order to build our multitouch application, we are going to create what is essentially a custom class in Flash. We begin this process by extending the “Application” class & setting up our new class to automatically inherit a multitouch framework that will manage touch events behind the scenes.

18
  public class main extends Application {

In this example, our GoogleMap object will be a 3-dimensional map object which will be nested inside a TouchSprite object.

19
20
    private var map:Map3D;
    private var map_holder:TouchSprite;

All Google Map components in the Actionscript API require an “API key” to function correctly.

22
    private var mapApiKey:String;

Our multitouch map will be fully interactive and allow the user to dynamically change the center of the map as well as the zoom level, roll and pitch in 3D space. To accommodate for this, we need to set up variables for these parameters that can be accessed from anywhere in our root class.

23
24
25
26
27
28
    private var currLat:Number;
    private var currLng:Number;
    private var currSc:Number;
    private var currAng:Number;
    private var currtiltAng:Number;
    private var stepSc:Number;

Now we’re ready to begin actually building our application. First, create a new public function.

30
    public function main () {

This next code block sets primary map properties, such as the API key and the center coordinates, so that the map can be pre-initialized before it is drawn on stage.

NOTE - Please register and download your own API key in order to run the attached demo.

31
32
33
      this.mapApiKey = "your API key here";
      this.currLng = -73.992062;
      this.currLat = 40.736072;

Sets the intital “zoom” level of the map (altitude), the initial “roll” and “pitch” angles of the view point (attitude) and sets the scale increments for the “flyto” action.

34
35
36
37
      this.currSc = 10;
      this.currAng = 0;
      this.currtiltAng = 0;
      this.stepSc = 1;

Since this application will be multitouch-enabled, we should hide the cursor on stage.

39
      Mouse.hide();

Our Google Map needs to be able to detect touch events. One method of enabling events on custom component objects, such as the Google Map component, is to create a simple holder TouchSprite and nest the map inside it. This allows all touches or gestures performed on the map to be efficiently captured.

41
      map_holder = new TouchSprite();

It is good practice to ensure that gestures and touch events that are exclusively used for an object be contained by that object when on stage.

42
      map_holder.blobContainerEnabled = true;

Now we want to set up listeners that allow users to drag the map using a single touch and fly to a location that is double-tapped. To enable this, TOUCH_MOVE and TOUCH_DOUBLE_TAP event listeners are added to the map_holder.

43
44
      map_holder.addEventListener(TouchEvent.TOUCH_MOVE, this.mapMove, false, 0, true);
      map_holder.addEventListener(TouchEvent.TOUCH_DOUBLE_TAP, this.mapFlyTo, false, 0, true);

To add other behaviors, such as the ability to scale, rotate and tilit the map in 3D, we need to enable gesture event detection on the map_holder object. To do this, add the GESTURE_SCALE, GESTURE_ROTATE and GESTURE_TILT gesture event listeners.

45
46
47
      map_holder.addEventListener(GestureEvent.GESTURE_SCALE, this.mapScale, false, 0, true);
      map_holder.addEventListener(GestureEvent.GESTURE_ROTATE, this.mapRotate, false, 0, true);
      map_holder.addEventListener(GestureEvent.GESTURE_TILT, this.mapTilt, false, 0, true);

The map_holder TouchSprite is added to the display list so that it (and the map object) is drawn on stage at runtime.

48
      this.addChild(map_holder);

Next we create a instance of the 3D map object, set the API key, then set the map size to match that of the stage size. The MAP_PREINITIALIZE map event listener is added so that once these initial parameters have been set and the map has pre-initialized, the function onMapPreInit() will be called. The map object is then added to the map_holder.

50
51
52
53
54
55
      map = new Map3D();
        map.key = mapApiKey;
        map.setSize(new Point(stage.stageWidth,stage.stageHeight));
        map.addEventListener(MapEvent.MAP_PREINITIALIZE, onMapPreInt);
      map_holder.addChild(map);
  }

Pre-Initializing The Map

Using the MAP_PREINITIALIZE event listener provides an efficient way to set a series of map parameters before the map is drawn on stage. Once map pre-initialization is complete, the onMapPreInt() function is called which sets the map zoom level, center of the map (using lattitude and longditute), the map type (physical) and the map view mode to display a 3D perspective relative to the virtual camera position and orientation in 3D space.

57
58
59
60
61
62
63
64
65
      private function onMapPreInt(event:MapEvent):void {
        var mOptions:MapOptions = new MapOptions();
          mOptions.zoom = currSc;
          mOptions.center = new LatLng(currLat,currLng);
          mOptions.mapType = MapType.PHYSICAL_MAP_TYPE;
          mOptions.viewMode =View.VIEWMODE_PERSPECTIVE;
          mOptions.attitude = new Attitude(currtiltAng,currAng,0);
       map.setInitOptions(mOptions);
     }

Dragging The Map

The mapMove() function retrieves the current location of the center of the map as latitude and longitude values then converts these to an x,y point. The change in x and y position is returned from the drag gesture and used to calculate the new location of the center point of the map. The x,y center point is then converted to a latitude and longitude and is used to set the center of the map to this new location.

67
68
69
70
71
72
      private function mapMove(e:TouchEvent):void {
        var point:Point = map.fromLatLngToPoint(map.getCenter());
        var newPoint:Point = new Point(point.x - e.dx, point.y - e.dy);
        var newLatLon:LatLng = map.fromPointToLatLng(newPoint);
        map.setCenter(newLatLon);
      }

Fly To New Map Center

When a double tap touch event is detected on the map, the mapFlyTo() function is called. This function takes the location of the double tap and zooms into into the location. To zoom into the map, a new value for the scale of the map is calculated by taking the current scale and adding the constant “stepSc”. This constant sets the number of steps in which the mapFlyTo() function will zoom into the map each time it is called. The x,y point returned from the TOUCH_DOUBLE_TAP TouchEvent is then converted to a latitude and longitude value by finding the 2D projection of the view port in 3D space. This is done to ensure that no null Lat Lng values are returned when the map is tiled in 3-dimensional space (when the view extends beyond the horizon).

74
75
76
77
78
79
      private function mapFlyto(e:TouchEvent):void {
        var newPoint:Point = new Point(e.stageX, e.stageY);
        var nLatLng:LatLng = map.fromViewportToLatLng(newPoint, true);
        currSc += stepSc;
        map.flyTo(nLatLng, currSc, new Attitude(currAng,currtiltAng,0), 2);
      }

Scaling The Map View

In this single-user multitouch application, we are going to use the “pinch” or “scale” gesture to control the zoom level of the map. Each time the scale gesture is detected on the map, the mapScale() function is called. The value returned from the GESTURE_SCALE gesture event is added to the current zoom level of the map to incrementally change the zoom level and smoothly scale in and out of the map.

81
82
83
84
      private function mapScale(e:GestureEvent):void {
        currSc += e.value;
        map.setZoom(currSc, true);
      }

Rotating The Map View

To rotate the map, two touch points are placed on the map and rotated relative to each other. The value of the GESTURE_ROTATE gesture event is returned, which measures the relative change in orientation of the two points. This is added to the current current angle value (currAng). The resultant value is then used to set the “Roll” of the map (angle of camera relative to the map footing) in 3D viewing space, allowing the map to rotate clockwise or counterclockwise (from the perspective of the observer).

86
87
88
89
      private function mapRotate(e:GestureEvent):void {
        currAng -= e.value;
        map.setAttitude(new Attitude(currAng,currtiltAng,0));
      }

Tilting The Map View

In this example, we are using the three finger drag gesture to tilt the view of the map in 3D. The GESTURE_TILT gesture event returns a value that measures the vertical motion of three dragged touch points. The function mapTilt() takes the value “tiltY” returned form the gesture event and adds it to the “currtiltAng” which is used to set the “pitch” of the map view in 3D space (angle of camera relative to the map horizon).

91
92
93
94
95
96
      private function mapTilt(e:GestureEvent):void {
        currtiltAng += e.tiltY;
        map.setAttitude(new Attitude(currAng,currtiltAng,0));
      }
   }
}

Summary

In this single-user multitouch map, we combine the drag and double tap touch events with the scale, rotate and tilt gestures to control the map in 3D space. Multiple gestures can occur at the same time allowing users to scale and rotate the map in one fluid motion if required. Gestures can be triggered using a selection of different techniques; for example, the scale gesture can be triggered by separating two clusters of touch points using both hands or by the separation of two touch points using a pinch gesture. The 3D tilt gesture can be triggered aggressively by dragging three touch points vertically on the map or used to make fine orientation changes by placing two fingers on the map and motioning vertically with another finger or the other hand. In all cases, users can transition between touch events and gestures or between gestures without having to remove all touch points. This allows for fluid transitions between tasks, leading to accessible and predictable map behavior and an elegant and easy-to-use interactive map.

Complete Example Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package {
  import flash.geom.*;
  import flash.ui.Mouse;

  import id.core.Application;
  import id.core.TouchSprite;
  import gl.events.TouchEvent;
  import gl.events.GestureEvent;

  import com.google.maps.LatLng;
  import com.google.maps.Map3D;
  import com.google.maps.MapEvent;
  import com.google.maps.MapOptions;
  import com.google.maps.MapType;
  import com.google.maps.View;
  import com.google.maps.geom.Attitude;

  public class main extends Application {
    private var map:Map3D;
    private var map_holder:TouchSprite;
   
    private var mapApiKey:String;
    private var currLat:Number;
    private var currLng:Number;
    private var currSc:Number;
    private var currAng:Number;
    private var currtiltAng:Number;
    private var stepSc:Number;
 
    public function main() {
      this.mapApiKey = "your API key here";
      this.currLng = -73.992062;
      this.currLat = 40.736072;
      this.currSc = 10;
      this.currAng = 0;
      this.currtiltAng = 0;
      this.stepSc = 1;
     
      Mouse.hide();
 
      map_holder = new TouchSprite();
        map_holder.blobContainerEnabled = true;
        map_holder.addEventListener(TouchEvent.TOUCH_MOVE, this.mapMove, false, 0, true);
        map_holder.addEventListener(TouchEvent.TOUCH_DOUBLE_TAP, this.mapFlyTo, false, 0, true);
        map_holder.addEventListener(GestureEvent.GESTURE_SCALE, this.mapScale, false, 0, true);
        map_holder.addEventListener(GestureEvent.GESTURE_ROTATE, this.mapRotate, false, 0, true);
        map_holder.addEventListener(GestureEvent.GESTURE_TILT, this.mapTilt, false, 0, true);
      this.addChild(map_holder);
 
      map = new Map3D();
        map.key = mapApiKey;
        map.setSize(new Point(stage.stageWidth,stage.stageHeight));
        map.addEventListener(MapEvent.MAP_PREINITIALIZE, onMapPreInt);
      map_holder.addChild(map);
    }
 
    private function onMapPreInt(event:MapEvent):void {
        var mOptions:MapOptions = new MapOptions();
        mOptions.zoom = currSc;
        mOptions.center = new LatLng(currLat,currLng);
        mOptions.mapType = MapType.PHYSICAL_MAP_TYPE;
        mOptions.viewMode =View.VIEWMODE_PERSPECTIVE;
        mOptions.attitude = new Attitude(currtiltAng,currAng,0);
        map.setInitOptions(mOptions);
    }
 
    private function mapMove(e:TouchEvent):void {
      var point:Point = map.fromLatLngToPoint(map.getCenter());
      var newPoint:Point = new Point(point.x - e.dx, point.y - e.dy);
      var newLatLon:LatLng = map.fromPointToLatLng(newPoint);
      map.setCenter(newLatLon);
    }
   
    private function mapFlyTo(e:TouchEvent):void {
      var newPoint:Point = new Point(e.stageX, e.stageY);
      var nLatLng:LatLng = map.fromViewportToLatLng(newPoint, true);
      currSc += stepSc;
      map.flyTo(nLatLng, currSc, new Attitude(currAng,currtiltAng,0), 2);
    }
   
    private function mapScale(e:GestureEvent):void {
      currSc += e.value;
      map.setZoom(currSc, true);
    }
   
    private function mapRotate(e:GestureEvent):void {
      currAng -= e.value;
      map.setAttitude(new Attitude(currAng,currtiltAng,0));
    }
   
    private function mapTilt(e:GestureEvent):void {
      currtiltAng += e.tiltY;
      map.setAttitude(new Attitude(currAng,currtiltAng,0));
    }
   
  }
}

Testing Your Application

There are various ways to test your multitouch application. If you have a multitouch imput device, events can be directly streamed to your Flash application via TUIO. If you do not have a device that can output multitouch events, you can use the built-in simulator in GestureWorks. To test your app in the main menu, go to Control > Test Movie or press “Ctrl + Enter” on the keyboard. Alternatively, you can publish your application as a self-contained executable by following these steps:

  • In your open Flash application “myApp.fla”, go to File > Publish Settings.
  • Then in the Formats tab, select “Windows Projector” or “Macintosh Projector,” then press the “Publish” button.
  • The application will be published to your app folder; from there, you can simply double click on myApp.exe (Windows) or myApp.app (Mac) and run your multitouch application.

NOTE - To build and test these tutorials on a computer without a touchscreen display, pressing “Shift” or “Control” while clicking on an object with the mouse will create additional touch points within the simulator. To remove touch points, simply click them.

Other Tutorials In The “Getting Started With Gestures” Series

These tutorials cover the basics of how to integrate touch and gesture events into your Flash applications:

Other Tutorials In The "Getting Started With Gestures" Series

These tutorials cover the basics of how to integrate touch and gesture events into your Flash applications: