There are many APIs out there to work with. If you start searching around Google, there are many people dedicated to helping people like you and me tie in different information and resources into Flash. It can take quite a long time just understanding a full API and be able to use ALL of it's capabilities. But that idea is way down the road. If you are like me, it's nice to find something to help you get off the ground.

Today I came across Emanuele Feronato's website, Italian geek and PROgrammer. He has a tutorial to get you started using the GoogleMaps API, so here is your first task in this tutorial:

1. Read Emanuele Feronato's tutorial and get Google Maps running in Flash.

Got it yet? Not yet? Don't worry I took me a little while to get everything downloaded, hooked in and sorted out.

Got it now? Awesome! Set your API key and everything? Great. This tutorial I'm going to try something different. Last time, I built up to the final code step by step. This one, I'll show you the final version of the code, and we'll step through it. Let me know which type of tutorial you like better and in the future, I'll try to keep a consistent style.

BUT! before I give you the code, it's time to add another function to the TUIO API, TUIOObject.as specifically. Now, hopefully you setup a new folder to hold your googleMaps.fla and AS script you are importing, for my example, in the 'app' folder, there will be a 'GoogleAPI' folder containing 'googlemap.as', the package file in my examples will be written for that setup. And copied over the "flash" folder like we did in the last tutorial (if you aren't familiar with what I'm talking about, refer to this post). Just make sure you have the returnBlobs() function ready to go in flash/events/TUIO.as. With me so far, right?

Now that the files are situated, open flash/events/TUIOObject.as

One bit of information we are going to need is the old and current; X and Y coordinated of the blobs interacting with the map. But if you look in line 23 of this file, it's not a public piece of information. We could use returnBlobs()[0].x, because x is a public variable, but if I were to use returnBlobs()[0].oldX, i'd just get an error. We need a new little function to return us the value of oldX and oldY, and while we are at it to make it symetrical, and for practice, lets make one that does X and Y as well.

Around line 95, I add this code:

  1. public function getTouchOldPoint():Point
  2. {
  3. var oldPoint:Point;
  4. oldPoint = TUIO_OBJECT.parent.globalToLocal(new Point(oldX, oldY));
  5. return oldPoint;
  6. }
  7. public function getTouchNewPoint():Point
  8. {
  9. var newPoint:Point;
  10. newPoint = TUIO_OBJECT.parent.globalToLocal(new Point(x, y));
  11. return newPoint;
  12. }

What does this do? getTouchOldPoint(), return a Point value. Since we'll need to know X and Y, why not make it one variable name to have to work with instead of two?

Now we are ready to get started with googlemap.as, load it up and insert this code:

  1. package app.GoogleAPI{
  2. import flash.display.Sprite;
  3. import flash.events.Event;
  4. import flash.geom.Point;
  5. import com.google.maps.LatLng;
  6. import com.google.maps.Map;
  7. import com.google.maps.MapEvent;
  8. import com.google.maps.MapType;
  9.  
  10. import flash.events.TUIO;// allows to connect to touchlib/tbeta
  11. import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
  12.  
  13. public class googlemap extends Sprite {
  14. private var map:Map = new Map();
  15.  
  16. public function googlemap() {
  17.  
  18. TUIO.init(this,'127.0.0.1',3000,'',true);
  19.  
  20. map.key="YOUR KEY";
  21. map.setSize(new Point(1024, 768));
  22. map.addEventListener(MapEvent.MAP_READY, onMapReady);
  23. this.addChild(map);
  24. function onMapReady(event:Event):void {
  25. map.setCenter(new LatLng(41.498155,-81.705322), 6, MapType.NORMAL_MAP_TYPE);
  26. }
  27.  
  28. this.addEventListener(TouchEvent.MOUSE_MOVE, onTouchMove);
  29.  
  30. }
  31. private function onTouchMove(evt:TouchEvent):void {
  32. var newTouchItem = TUIO.returnBlobs()[0];
  33. var shiftX = (newTouchItem.getTouchOldPoint().x - newTouchItem.getTouchNewPoint().x);
  34. var shiftY = (newTouchItem.getTouchOldPoint().y - newTouchItem.getTouchNewPoint().y);
  35. var movePt:Point = new Point(shiftX * 2, shiftY * 2);
  36. map.panBy(movePt);
  37. }
  38. }
  39. }

So let's take a look at it.

The first part of it is all pretty normal stuff. Declare the needed classes, google maps, TUIO, Sprite, Event for google maps, geom.Point. declare class name and it's extension. make a new private var for the new Map(); We do not want any outside classes directly effecting this variable. Start your googlemap() function and start up TUIO.

Addition note: for map.setSize(new Point(1024, 768)); it was originally set to map.setSize(new Point(stage.stageWidth, stage.stageHeight)); unfortunetly, when you try to load this externally from an outside flash container, it cannot recognize the stage width and height, so I have it hard coded to my screen's resolution, feel free to change it to your own.

Then at line 28, it gets to the fun part. We begin with just wanting to move the map with a touch point.

  1. this.addEventListener(TouchEvent.MOUSE_MOVE, onTouchMove);

This line listens for any TouchEvent movement on the stage, for this first iteration, we'll just use the very first blob to control the movement of the map. After studying the Google Maps API, I learned you can move the map with a function called panBy(Point). It pans the map to a new point, and we can gather that value from a Touch MOVE, by comparing where the blob was to where it is.

  1. var newTouchItem = TUIO.returnBlobs()[0];
  2. var shiftX = (newTouchItem.getTouchOldPoint().x - newTouchItem.getTouchNewPoint().x);
  3. var shiftY = (newTouchItem.getTouchOldPoint().y - newTouchItem.getTouchNewPoint().y);
  4. var movePt:Point = new Point(shiftX * 2, shiftY * 2);

First we need to know what to get, TUIO.returnBlobs(), returns an array of all the active blobs at that moment, then TUIO.returnBlobs()[0] give you the Touch Object of just the first blob to work with. Time to calculate, newTouchItem.getTouchOldPoint().x uses one of the new functions we put into TUIOObject.as, it'll get us the previous X coordinate of the very first blob in the available array of blob items. We take the old touch point's X and subtract it from the new touch point's X, this gived us our shift in the X coordinate after a TouchMOVE. Then it's just the matter of doing the same to the Y coordinate, and putting that into a new Point variable (movePt) for panBy to use.

Note: I multiplied both values by 2 to adjust the speed of the movement, you may want to test out different values until you are happy with the speed of the map's follow compared to the touch point.

  1. map.panBy(movePt);

Then panBy just takes the new value and move the map to that final location. Easy, right?

You want something more complicated? Alright, let's make it zoom!
Watch out for line 58 if you copy and paste, my code formatting plugin may change > to >

  1. package app.GoogleAPI{
  2. import flash.display.Sprite;
  3. import flash.events.Event;
  4. import flash.geom.Point;
  5. import com.google.maps.LatLng;
  6. import com.google.maps.Map;
  7. import com.google.maps.MapEvent;
  8. import com.google.maps.MapType;
  9.  
  10. import flash.events.TUIO;// allows to connect to touchlib/tbeta
  11. import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
  12.  
  13. public class googlemap extends Sprite {
  14. private var map:Map = new Map();
  15. private var origPt1:Point;
  16. private var origPt2:Point;
  17. private var origZoom:Number;
  18.  
  19. public function googlemap() {
  20.  
  21. TUIO.init(this,'127.0.0.1',3000,'',true);
  22.  
  23. map.key="YOUR KEY";
  24. map.setSize(new Point(1024, 768));
  25. map.addEventListener(MapEvent.MAP_READY, onMapReady);
  26. this.addChild(map);
  27.  
  28. function onMapReady(event:Event):void {
  29. map.setCenter(new LatLng(41.498155,-81.705322), 6, MapType.NORMAL_MAP_TYPE);
  30. }
  31.  
  32. this.addEventListener(TouchEvent.MOUSE_DOWN, onTouchUpDown);
  33. this.addEventListener(TouchEvent.MOUSE_UP, onTouchUpDown);
  34. this.addEventListener(TouchEvent.MOUSE_MOVE, onTouchMove);
  35. }
  36. private function onTouchUpDown(evt:TouchEvent):void {
  37. if (TUIO.returnBlobs().length == 2) {
  38. var tuioobjOriginal1 = TUIO.returnBlobs()[0];
  39. var tuioobjOriginal2 = TUIO.returnBlobs()[1];
  40. origPt1 = new Point(tuioobjOriginal1.getTouchNewPoint().x, tuioobjOriginal1.getTouchNewPoint().y);
  41. origPt2 = new Point(tuioobjOriginal2.getTouchNewPoint().x, tuioobjOriginal2.getTouchNewPoint().y);
  42. origZoom = map.getZoom();
  43. }
  44. }
  45. private function onTouchMove(evt:TouchEvent):void {
  46. if (TUIO.returnBlobs().length == 2) {
  47. var tuioobj1 = TUIO.returnBlobs()[0];
  48. var tuioobj2 = TUIO.returnBlobs()[1];
  49.  
  50. var curPt1:Point = new Point(tuioobj1.getTouchNewPoint().x, tuioobj1.getTouchNewPoint().y);
  51. var curPt2:Point = new Point(tuioobj2.getTouchNewPoint().x, tuioobj2.getTouchNewPoint().y);
  52.  
  53. var len1:Number = Point.distance(origPt1, origPt2);
  54. var len2:Number = Point.distance(curPt1, curPt2);
  55.  
  56. var newscale:Number = len2/len1;
  57.  
  58. if(newscale >= 1) {
  59. map.setZoom(origZoom + (Math.floor(newscale)-1));
  60. } else {
  61. newscale = len1/len2;
  62. map.setZoom(origZoom - (Math.floor(newscale)-1));
  63. }
  64. } else {
  65. var newTouchItem = TUIO.returnBlobs()[0];
  66. var shiftX = (newTouchItem.getTouchOldPoint().x - newTouchItem.getTouchNewPoint().x);
  67. var shiftY = (newTouchItem.getTouchOldPoint().y - newTouchItem.getTouchNewPoint().y);
  68. var movePt:Point = new Point(shiftX * 2, shiftY * 2);
  69. map.panBy(movePt);
  70. }
  71. }
  72. }
  73. }

Lot more to go though, so let's break it down, first thing to point out:

  1. private var origPt1:Point;
  2. private var origPt2:Point;
  3. private var origZoom:Number;
  4. private var newZoomLevel:Number;

There are some variables we need to set aside for multiple function to use. To zoom in and out in google maps, I followed and modified the code from Scalable.as found in the \AS3\int\app\core\action folder in the Touchlib SVN build. We'll need origPt1 and origPt2 to compare the size between 2 touch points applied to the map. origZoom is to keep track of what the starting level of the google map's zoom is at.

  1. this.addEventListener(TouchEvent.MOUSE_DOWN, onTouchUpDown);
  2. this.addEventListener(TouchEvent.MOUSE_UP, onTouchUpDown);
  3. this.addEventListener(TouchEvent.MOUSE_MOVE, onTouchMove);

This time around we'll need to collect and verify information a the Touch DOWN and Touch UP events. And the same code cam be applied to Touch DOWN and Touch UP, because it only need to set a few variables for comparison purposes for the TouchMOVE event.

  1. private function onTouchUpDown(evt:TouchEvent):void {
  2. if (TUIO.returnBlobs().length == 2) {
  3. var tuioobjOriginal1 = TUIO.returnBlobs()[0];
  4. var tuioobjOriginal2 = TUIO.returnBlobs()[1];
  5. origPt1 = new Point(tuioobjOriginal1.getTouchNewPoint().x, tuioobjOriginal1.getTouchNewPoint().y);
  6. origPt2 = new Point(tuioobjOriginal2.getTouchNewPoint().x, tuioobjOriginal2.getTouchNewPoint().y);
  7. origZoom = map.getZoom();
  8. }
  9. }

onTouchUpDown, when it learns that there are 2 blobs on the stage, we ultilize getTouchNewPoint() we created earlier to get the initial X and Y coordinated of the 2 points. As well as getting what zoom level google map is positioned at, at that moment. These create our starting point values for comparing the scale on our Touch MOVE event.

  1. if (TUIO.returnBlobs().length == 2) {
  2. var tuioobj1 = TUIO.returnBlobs()[0];
  3. var tuioobj2 = TUIO.returnBlobs()[1];
  4.  
  5. var curPt1:Point = new Point(tuioobj1.getTouchNewPoint().x, tuioobj1.getTouchNewPoint().y);
  6. var curPt2:Point = new Point(tuioobj2.getTouchNewPoint().x, tuioobj2.getTouchNewPoint().y);
  7.  
  8. var len1:Number = Point.distance(origPt1, origPt2);
  9. var len2:Number = Point.distance(curPt1, curPt2);

In the Touch MOVE event is where the bulk of the calculations exist, thanks to the creator of Scalable, I was able to get this working rather quickly. Once it know there are only two touch points active on the map, it begins gathering the needed information. Starting with the X and Y of both blobs. It builds a Point object to hold both parts of the information. Then len1 calculates the distance between the two original blob locations, then len2 calculated the distance between the same blobs after it has moved, but since origPt1 and 2 are held outside of the function, they can retain the information for continued comparison until another blob is added or removed.

  1. var newscale:Number = len2/len1;
  2.  
  3. if(newscale >= 1) {
  4. map.setZoom(origZoom + (Math.floor(newscale)-1));
  5. } else {
  6. newscale = len1/len2;
  7. map.setZoom(origZoom - (Math.floor(newscale)-1));
  8. }

So how does it know which way to scale? New variable, newscale, divides the NEW distance from the OLD distance. But now we need to do a little comparison to help coordinate the scaling, we only want it to bump up after a certian amount of scaling, not all the time, so originally the calculation used was curScale * len2/len1 with curScale typically being 100 because Scalable.as used scaleX and scaleY to change the object's scale. But for this we need to get the information to a precreated function. The zoom function ranges from 1 to 19 I believe, so for this calculation. Just before that though, we look to see if we are only scaling up with if(newscale >= 1), when it is, we calculate origZoom + (Math.floor(newscale)-1), something worth watching would may be putting a trace under that line showing you the calculations with and without Math.floor(). Now, if we are scaling down, that means newscale will be a decimal value which we cannot Math.floor() so once we are through the intial if statement we can swap the calculation for newscale to find the reverse division between the two blob lengths. Once that is calculated, we just subtract that from the origZoom variable instead of adding it. That calculation's result is given to map.setZoom() to them zoom the map.

If there are not 2 active touchpoints on the stage, either more or less, the Touch MOVE event then just uses the very first touch point to move the map as in our first example.

This, I hope, has gotten you started with using GoogleMap's Flash API with multi-touch. Take a look at the documents for the API, there is a lot you can do with it. You just need to start taking those steps closer to your final goal. And I just have to add, writing out a tutorial of your code really make you start thinking more critically about how you work problems out and clean up your code. I highly recommend trying it out.

This code had been uploaded to my Google Code Page.