Maps

0
244

Several geocoding systems and companies offer web services for the consumer market. They all provide similar features. A map is received. It is drawn or a composite of satellite pictures or street tiles is drawn, the latter being more common for mobile devices. It can pan and zoom. Geographical locations or points of interest are represented in the form of markers. Additional features include custom itineraries, the display of specific areas in color, driving and biking directions, and business searches.

Some of the better-known geocoding systems are Google Maps, Yahoo! Maps, Bing Maps, GeoNames, and USC Geocoder. As the technology is rapidly growing, this list may soon expand or change. A lot of map services get their information from NAVTEQ and Tele Atlas, companies that sell databases of geodata, or from MaxMind which sells IP geolocation data. Google now has its own full set of geodata, gathered by its streetview cars.

Launching Google Maps

As we previously discussed, you can collect a point location (latitude, longitude) using the Geolocation class, and pass it to the device using a URI handler. It then presents the user with the option of using the native Maps application or launching Google Maps in the browser:

<uses-permission android:name=”android.permission.INTERNET” />
import flash.events.GeolocationEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.sensors.Geolocation;
function onTravel(event:GeolocationEvent):void {
geolocation.removeEventListener(GeolocationEvent.UPDATE, onTravel);
var long:String = event.longitude.toString();
var lat:String = event.latitude.toString();
navigateToURL(
new URLRequest(“http://maps.google.com/?q=” + lat + “,” + long));
}

Note that if you navigate to http://maps.yahoo.com instead, launching the native Google Maps is not an option.

The major hurdle with this approach is that your application is now in the background and there is no direct way to go back to it unless you press the device’s native back button.

The Android SDK has a library for embedding maps into native applications with interactivity. AIR doesn’t support this feature at the time of this writing, but there are many other ways to offer a map experience to your audience. To demonstrate some of the map features within AIR, we will use the Yahoo! Maps Web Services (http://developer.yahoo.com/maps) and Google Maps API family (http://code.google.com/apis/maps/).

Static Maps

A static map may be sufficient for your needs. It provides a snapshot of a location; although it doesn’t offer pan or zoom, it does load relatively quickly, even over GPS.

The Yahoo! Map Image API

The Yahoo! Map Image API from Yahoo! Maps (http://developer.yahoo.com/maps/rest/V1/) provides a reference to a static map image based on user-specified parameters. This API requires an applicationID. It doesn’t set a restriction on how to use the service, it serves images up to 1,024×1,024, and it has few customizable options.

To use the API, send a URLRequest with your parameters. In return, you receive the path to the image which you then load using a Loader object. The next example uses the point location from geolocation, the stage dimensions for the image size, and 1 for street level (zoom goes up to 12 for country level):

import flash.display.Loader;
import flash.events.GeolocationEvent;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.sensors.Geolocation;
var geolocation:Geolocation;
const YAHOO_URL:String =
“http://local.yahooapis.com/MapsService/V1/mapImage”;
const applicationID:String = “YOUR_YAHOO_APP_ID”;
var urlLoader:URLLoader;
var loader:Loader;
function findLocation():void {
if (Geolocation.isSupported) {
geolocation = new Geolocation();
geolocation.addEventListener(GeolocationEvent.UPDATE, onTravel);
}
}

function onTravel(event:GeolocationEvent):void {
var request:String = “?appid=YOUR_APPI”
+ “&latitude=” + event.latitude
+ “&longitude=” + event.longitude
+ “&zoom=1”
+ “&image_height=” + stage.stageHeight
+ “&image_width=” + stage.stageWidth;
urlLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onXMLReceived);
urlLoader.load(new URLRequest(YAHOO_URL + request));
}

function onXMLReceived(event:Event):void {
urlLoader.removeEventListener(Event.COMPLETE, onXMLReceived);
geolocation.removeEventListener(GeolocationEvent.UPDATE, onTravel);
var xml:XML = XML(event.currentTarget.data);
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest(xml));
}

function onLoaded(event:Event):void {
event.currentTarget.removeEventListener(Event.COMPLETE, onLoaded);
this.addChild(event.currentTarget.content);
}

You should see a map of where you are currently located.

The Google Static Maps API

The Google Static Maps API (http://code.google.com/apis/maps/documentation/staticmaps/) offers more features than the Yahoo! product, but at the time of this writing, it enforces a rule whereby static maps can only be displayed as browser content unless you purchase a Google Maps API Premier license. An AIR application is not considered browser content. Read the terms carefully before developing a commercial product using this API.

With this service, a standard HTTP request returns an image with the settings of your choice.

The required parameters are as follows:

  • center for location as an address or latitude/longitude (not required with marker)
  • zoom from 0 for the Earth to 21 for a building (not required with marker)
  • size (up to 640×640 pixels)
  • sensor (with or without use of GPS locator)

The maximum image size is 640×640. Unless you scale the image up in size, it will not fill the screen on most Android devices. Optional parameters are mobile, format, map type, language, markers, visible, and path.

The following example requests a 480×640 image of Paris centered on the Eiffel Tower:

<uses-permission android:name=”android.permission.INTERNET” />
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
const GOOGLE_URL:String = “http://maps.google.com/maps/api/staticmap?”
function loadStaticImage():void {
var request:String = “center=Eiffel+Tower,Paris,France
&zoom=16&size=480×640&sensor=false”;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
loader.load(new URLRequest(GOOGLE_URL + request));
}
function imageLoaded(event:Event):void {
event.currentTarget.removeEventListener(Event.COMPLETE, imageLoaded);
addChild(event.currentTarget.content);
}

You should see on your screen the map of Paris, as shown in Figure 10-3.

Let’s make another request using a dynamic location. The sensor parameter is now set to true and we are adding the mobile parameter. The image size is also dynamic, choosing whichever value is the smallest between Google restricted values and our stage size. And we are now using the hybrid version of the maptype:

import flash.display.Loader;
import flash.net.URLRequest;
import flash.sensors.Geolocation;
const GOOGLE_URL:String = “http://maps.google.com/maps/api/staticmap?”
var geolocation:Geolocation = new Geolocation();
geolocation.addEventListener(GeolocationEvent.UPDATE, onTravel);

function onTravel(event:GeolocationEvent):void {
geolocation.removeEventListener(GeolocationEvent.UPDATE, onTravel);
loadStaticImage(event.latitude, event.longitude);
}
function loadStaticImage(lat:Number, long:Number):void {
var width:int = Math.min(640, stage.stageWidth);
var height:int = Math.min(640, stage.stageHeight);
var request:String = “center=” + lat + “,” + long +
“&zoom=15
&size=” + width + “x” + height +
“&maptype=hybrid&mobile=true&sensor=true”;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
loader.load(new URLRequest(GOOGLE_URL + request));
}
function imageLoaded(event:Event):void {
event.currentTarget.removeEventListener(Event.COMPLETE, imageLoaded);
addChild(event.currentTarget.content);
}

Map of Paris centered on the Eiffel Tower

The center parameter can be substituted for one or multiple markers. A marker can be given a color and a label, or it can be customized. And we are now using the road map version of the maptype:

function loadStaticImage(lat:Number, long:Number):void {
var width:int = Math.min(640, stage.StageWidth);
var height:int = Math.min(640, stage.StageHeight);
var request:String = “markers=size:large|color:blue|label:L|”
+ lat + “,” + long
+ “&zoom=16″
+ &size=” + width + “x” + height +
“&maptype=roadmap&mobile=true&sensor=true”;
var loader:Loader = new Loader();
addChild(loader);
loader.load(new URLRequest(googleURL + request));
}

This is again a low-impact but limited solution. The user only sees an image that cannot be scaled and is only given basic information. Let’s now look at using actual maps.

Dynamic Maps

Yahoo! and Google both provide well-documented AS3 libraries for use with their map APIs. The only restriction for both is a maximum of 50,000 uses per day.

Maps are slow to initialize. Create placeholder art to display instead of the default gray rectangle, and display an attractive loading animation. Do not forget to remove the art after the map appears. Any art under the map would slow down its performance.

The Google Maps API for Flash

With the Google Maps API for Flash (http://code.google.com/apis/maps/documentation/flash/), Flex developers can embed Google maps in Flash applications. Sign up for a Google Maps API key and download the Flash SDK. Use version 20 or up (map_1_20.swc or map_flex_1_20.swc), as version 19 has a known issue with ResizeE vent. Add the path to the .swc file in the library path and set Default Linkage to “Merged into code”. As you will see, this API offers a wealth of options that are easy to implement.

To set the library path in Flash Professional, go to File→Publish Settings. Click the tool icon next to Script. Select the Library Path tab and click the Flash icon to navigate to the .swc file. Once you’ve imported the file, change Default Linkage to “Merged into code”.

In Flash Builder, right-click your project, go to Properties→ ActionScript Build Path, and click Add SWC to navigate to the .swc file. “Merged into code” is the default setting.

Create a Map object as well as key and url properties. Entering both your API key and the site URL you submitted when you applied for the key is required even though you are not displaying the map in your website but rather as a standalone Android application.

The sensor parameter, also required, states whether you use a GPS sensor. It needs to be a string, not a boolean. The map size, defined by setSize, is set dynamically to the dimensions of the stage.

When the map is ready, the geolocation listener is set up. After the first update is received, the setCenter function is called with location, zoom level, and the type of map to use. Finally, the zoom control is added. Figure 10-4 shows the result:

My current location

<uses-permission android:name=”android.permission.INTERNET” />
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.MapType;
import com.google.maps.LatLng;
import flash.geom.Point;
import flash.sensors.Geolocation;
import flash.events.GeolocationEvent;
import com.google.maps.controls.ZoomControl;
const KEY:String = YOUR_API_KEY;
const SITE:String = YOUR_SITE;
var map:Map;
var geolocation:Geolocation;
map = new Map();
map.key = KEY;
map.url = SITE;
map.sensor = “true”;
map.setSize(new Point(stage.stageWidth, stage.stageHeight));
map.addEventListener(MapEvent.MAP_READY, onMapReady);
function onMapReady(event:MapEvent):void {
geolocation = new Geolocation();
geolocation.addEventListener(GeolocationEvent.UPDATE, onTravel);
addChild(map);
}
function onTravel(event:GeolocationEvent):void {
geolocation.removeEventListener(GeolocationEvent.UPDATE, onTravel);
map.setCenter(new LatLng(event.latitude, event.longitude),
18, MapType.NORMAL_MAP_TYPE);
map.addControl(new ZoomControl());
}

Add a marker as landmarks and navigation control. Here the marker is customized to have a shadow, a blue color, and a defined radius. When you click on it, an information window opens with text content:

import com.google.maps.overlays.Marker;
import com.google.maps.overlays.MarkerOptions;
import com.google.maps.InfoWindowOptions;
var options:Object = {hasShadow:true,
fillStyle: new FillStyle({color:0x0099FF, alpha:0.75}),
radius:12
};
var marker:Marker =
new Marker(new LatLng(45.7924, 15.9696), new MarkerOptions(options));
marker.addEventListener(MapMouseEvent.CLICK, markerClicked);
map.addOverlay(marker);
function markerClicked(event:MapMouseEvent):void {
event.currentTarget.openInfoWindow
(new InfoWindowOptions({content:”hello”});
}

Styled Maps support

In October 2010, Google announced support for Styled Maps on Flash, included in Flash SDK version 20 and up (see http://code.google.com/apis/maps/documentation/flash/maptypes.html#StyledMaps). This addition gives you control over color scheme and customization of markers and controls. It makes your map look more unique or match your brand and design. You can also write or draw over the map. The Google Geo Developers Blog (http://googlegeodevelopers.blogspot.com/2010/10/five-great-styled-maps-examples.html) shows some examples of how Styled Maps has been used.

Google Maps 5

Google Maps 5 was released in December 2010. It provides 3D building rendering, dynamic vector-based map drawing, and offline reliability. If you would like to see it supported in AIR, file a software request on the Adobe site, http://www.mobilecrunch.com/2010/12/16/google-maps-5-with-3d-buildings-now-available-for-android/.