As part of my MAX session, I’ve been playing around with the camera API and wanted to pull some of the Exif data around geolocation. My first thought was that I could use the geolocation APIs in AIR to inject Exif data into the image from the camera. But the way that the AIR for Android Camera API works (which makes sense) is that when you take a picture, it creates a MediaPromise, which is a new class in AIR for Android that is similar to a FilePromise. That code includes a bunch of Exif data that Android adds. The only issue with the GPS coordinates are that they’re in sexagesimal format and need to be converted into decimal.
There are actually two ways to get images into your AIR application. The first is using the flash.media.CameraUI class. That brings up the camera controls and lets the user take a picture directly. When the user clicks “OK” on the camera, it saves the file to the SD card and then passes the file reference back to AIR where you can program an event handler to respond:
public var cameraUI:CameraUI; protected function onCreationComplete():void { if(CameraUI.isSupported) { cameraUI = new CameraUI(); cameraUI.addEventListener(MediaEvent.COMPLETE,onComplete); } } |
Then all that has to be done is to call the launch() method and that pops up the camera controls. When the user confirms the photo, the MediaEvent.COMPLETE handler deals with the photo. Or in this case, the file reference.
protected function btn_clickHandler(event:MouseEvent):void { cameraUI.launch(MediaType.IMAGE); } |
The other way to get access to photos is to pull them from the phone’s memory. You can do that with the flash.media.CameraRoll API. It’s a very similar process to the CameraUI. Instead of a complete event, listen for a select event, and to bring up the library of images, call the browseForImage() method.
public var cameraRoll:CameraRoll; protected function onCreationComplete():void { if(CameraRoll.supportsBrowseForImage) { cameraRoll = new CameraRoll(); cameraRoll.addEventListener(MediaEvent.SELECT,onSelect); } } protected function btn_clickHandler(event:MouseEvent):void { cameraRoll.browseForImage(); } |
Now once the image has been taken or selected, we can pull the Exif data from it. Both the complete event and the select event will look the same because they both create a MediaPromise. I’m using the Exif library here, which uses the load() method and an event handler to deal with the Exif data when it loads.
protected function onSelect(event:MediaEvent):void { var request:URLRequest = new URLRequest(event.data.file.url); var exif:ExifLoader = new ExifLoader(); exif.addEventListener(Event.COMPLETE,onExifComplete); exif.load(request); } |
The parsing of the Exif data is pretty straightforward. Once it has been parsed, pulling out the specific tags by name for Latitude and Longitude, all that is left is to convert the sexagesimal latitude and longitude to decimal degrees.
protected function onExifComplete(event:Event):void { var exif:ExifInfo = event.currentTarget.exif as ExifInfo; var gpsIfd:IFD = exif.ifds.gps; // get the array of GPS coordinates from the Exif data var exifLat:Array = gpsIfd["GPSLatitude"] as Array; var exifLon:Array = gpsIfd["GPSLongitude"] as Array; // get the decimal degrees for latitude/longitude var latitude:Number = convertSexagesimalToDecimal(exifLat[0],exifLat[1],exifLat[2],gpsIfd["GPSLatitudeRef"]); var longitude:Number = convertSexagesimalToDecimal(exifLon[0],exifLon[1],exifLon[2],gpsIfd["GPSLongitudeRef"]); } protected function convertSexagesimalToDecimal(degrees:int,minutes:int,seconds:Number,reference:String):Number { // do the conversion to decimal degrees var decimal:Number = degrees + (minutes/60) + (seconds / 3600); // figure out whether we need to use negative latitude or longitude if(reference == "S" || reference == "E") { return decimal * -1; } else { return decimal; } } |
So that’s a crash course in using the camera API on AIR for Android and then extracting decimal degrees out of Android’s existing Exif data.
Tweet