how to use 3D map Actionscript class in mxml file for display map.
- by nemade-vipin
hello friends,
I have created the application in which I have to use 3D map Action Script class in mxml file to display a map in form. that is in tab navigator last tab.
My ActionScript 3D map class is(FlyingDirections):-
package src.SBTSCoreObject
{
import src.SBTSCoreObject.JSONDecoder;
import com.google.maps.InfoWindowOptions;
import com.google.maps.LatLng;
import com.google.maps.LatLngBounds;
import com.google.maps.Map3D;
import com.google.maps.MapEvent;
import com.google.maps.MapOptions;
import com.google.maps.MapType;
import com.google.maps.MapUtil;
import com.google.maps.View;
import com.google.maps.controls.NavigationControl;
import com.google.maps.geom.Attitude;
import com.google.maps.interfaces.IPolyline;
import com.google.maps.overlays.Marker;
import com.google.maps.overlays.MarkerOptions;
import com.google.maps.services.Directions;
import com.google.maps.services.DirectionsEvent;
import com.google.maps.services.Route;
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.filters.DropShadowFilter;
import flash.geom.Point;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.navigateToURL;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.Timer;
import flash.utils.getTimer;
public class FlyingDirections extends Map3D
{
/**
* Panoramio home page.
*/
private static const PANORAMIO_HOME:String = "http://www.panoramio.com/";
/**
* The icon for the car.
*/
[Embed("assets/car-icon-24px.png")]
private static const Car:Class;
/**
* The Panoramio icon.
*/
[Embed("assets/iw_panoramio.png")]
private static const PanoramioIcon:Class;
/**
* We animate a zoom in to the start the route before the car starts
* to move. This constant sets the time in seconds over which this
* zoom occurs.
*/
private static const LEAD_IN_DURATION:Number = 3;
/**
* Duration of the trip in seconds.
*/
private static const TRIP_DURATION:Number = 40;
/**
* Constants that define the geometry of the Panoramio image markers.
*/
private static const BORDER_T:Number = 3;
private static const BORDER_L:Number = 10;
private static const BORDER_R:Number = 10;
private static const BORDER_B:Number = 3;
private static const GAP_T:Number = 2;
private static const GAP_B:Number = 1;
private static const IMAGE_SCALE:Number = 1;
/**
* Trajectory that the camera follows over time. Each element is an object
* containing properties used to generate parameter values for flyTo(..).
* fraction = 0 corresponds to the start of the trip; fraction = 1
* correspondsto the end of the trip.
*/
private var FLY_TRAJECTORY:Array = [
{ fraction: 0, zoom: 6, attitude: new Attitude(0, 0, 0) },
{ fraction: 0.2, zoom: 8.5, attitude: new Attitude(30, 30, 0) },
{ fraction: 0.5, zoom: 9, attitude: new Attitude(30, 40, 0) },
{ fraction: 1, zoom: 8, attitude: new Attitude(50, 50, 0) },
{ fraction: 1.1, zoom: 8, attitude: new Attitude(130, 50, 0) },
{ fraction: 1.2, zoom: 8, attitude: new Attitude(220, 50, 0) },
];
/**
* Number of panaramio photos for which we load data. We'll select a
* subset of these approximately evenly spaced along the route.
*/
private static const NUM_GEOTAGGED_PHOTOS:int = 50;
/**
* Number of panaramio photos that we actually show.
*/
private static const NUM_SHOWN_PHOTOS:int = 7;
/**
* Scaling between real trip time and animation time.
*/
private static const SCALE_TIME:Number = 0.001;
/**
* getTimer() value at the instant that we start the trip. If this is 0 then
* we have not yet started the car moving.
*/
private var startTimer:int = 0;
/**
* The current route.
*/
private var route:Route;
/**
* The polyline for the route.
*/
private var polyline:IPolyline;
/**
* The car marker.
*/
private var marker:Marker;
/**
* The cumulative duration in seconds over each step in the route.
* cumulativeStepDuration[0] is 0; cumulativeStepDuration[1] adds the
* duration of step 0; cumulativeStepDuration[2] adds the duration
* of step 1; etc.
*/
private var cumulativeStepDuration:/*Number*/Array = [];
/**
* The cumulative distance in metres over each vertex in the route polyline.
* cumulativeVertexDistance[0] is 0; cumulativeVertexDistance[1] adds the
* distance to vertex 1; cumulativeVertexDistance[2] adds the distance to
* vertex 2; etc.
*/
private var cumulativeVertexDistance:Array;
/**
* Array of photos loaded from Panoramio. This array has the same format as
* the 'photos' property within the JSON returned by the Panoramio API
* (see http://www.panoramio.com/api/), with additional properties added to
* individual photo elements to hold the loader structures that fetch
* the actual images.
*/
private var photos:Array = [];
/**
* Array of polyline vertices, where each element is in world coordinates.
* Several computations can be faster if we can use world coordinates
* instead of LatLng coordinates.
*/
private var worldPoly:/*Point*/Array;
/**
* Whether the start button has been pressed.
*/
private var startButtonPressed:Boolean = false;
/**
* Saved event from onDirectionsSuccess call.
*/
private var directionsSuccessEvent:DirectionsEvent = null;
/**
* Start button.
*/
private var startButton:Sprite;
/**
* Alpha value used for the Panoramio image markers.
*/
private var markerAlpha:Number = 0;
/**
* Index of the current driving direction step. Used to update the
* info window content each time we progress to a new step.
*/
private var currentStepIndex:int = -1;
/**
* The fly directions map constructor.
*
* @constructor
*/
public function FlyingDirections() {
key="ABQIAAAA7QUChpcnvnmXxsjC7s1fCxQGj0PqsCtxKvarsoS-iqLdqZSKfxTd7Xf-2rEc_PC9o8IsJde80Wnj4g";
super();
addEventListener(MapEvent.MAP_PREINITIALIZE, onMapPreinitialize);
addEventListener(MapEvent.MAP_READY, onMapReady);
}
/**
* Handles map preintialize. Initializes the map center and zoom level.
*
* @param event The map event.
*/
private function onMapPreinitialize(event:MapEvent):void {
setInitOptions(new MapOptions({
center: new LatLng(-26.1, 135.1),
zoom: 4,
viewMode: View.VIEWMODE_PERSPECTIVE,
mapType:MapType.PHYSICAL_MAP_TYPE
}));
}
/**
* Handles map ready and looks up directions.
*
* @param event The map event.
*/
private function onMapReady(event:MapEvent):void {
enableScrollWheelZoom();
enableContinuousZoom();
addControl(new NavigationControl());
// The driving animation will be updated on every frame.
addEventListener(Event.ENTER_FRAME, enterFrame);
addStartButton();
// We start the directions loading now, so that we're ready to go when
// the user hits the start button.
var directions:Directions = new Directions();
directions.addEventListener(
DirectionsEvent.DIRECTIONS_SUCCESS, onDirectionsSuccess);
directions.addEventListener(
DirectionsEvent.DIRECTIONS_FAILURE, onDirectionsFailure);
directions.load("48 Pirrama Rd, Pyrmont, NSW to Byron Bay, NSW");
}
/**
* Adds a big blue start button.
*/
private function addStartButton():void {
startButton = new Sprite();
startButton.buttonMode = true;
startButton.addEventListener(MouseEvent.CLICK, onStartClick);
startButton.graphics.beginFill(0x1871ce);
startButton.graphics.drawRoundRect(0, 0, 150, 100, 10, 10);
startButton.graphics.endFill();
var startField:TextField = new TextField();
startField.autoSize = TextFieldAutoSize.LEFT;
startField.defaultTextFormat =
new TextFormat("_sans", 20, 0xffffff, true);
startField.text = "Start!";
startButton.addChild(startField);
startField.x = 0.5 * (startButton.width - startField.width);
startField.y = 0.5 * (startButton.height - startField.height);
startButton.filters = [ new DropShadowFilter() ];
var container:DisplayObjectContainer =
getDisplayObject() as DisplayObjectContainer;
container.addChild(startButton);
startButton.x = 0.5 * (container.width - startButton.width);
startButton.y = 0.5 * (container.height - startButton.height);
var panoField:TextField = new TextField();
panoField.autoSize = TextFieldAutoSize.LEFT;
panoField.defaultTextFormat =
new TextFormat("_sans", 11, 0x000000, true);
panoField.text = "Photos provided by Panoramio are under the copyright of their owners.";
container.addChild(panoField);
panoField.x = container.width - panoField.width - 5;
panoField.y = 5;
}
/**
* Handles directions success. Starts flying the route if everything
* is ready.
*
* @param event The directions event.
*/
private function onDirectionsSuccess(event:DirectionsEvent):void {
directionsSuccessEvent = event;
flyRouteIfReady();
}
/**
* Handles click on the start button. Starts flying the route if everything
* is ready.
*/
private function onStartClick(event:MouseEvent):void {
startButton.removeEventListener(MouseEvent.CLICK, onStartClick);
var container:DisplayObjectContainer =
getDisplayObject() as DisplayObjectContainer;
container.removeChild(startButton);
startButtonPressed = true;
flyRouteIfReady();
}
/**
* If we have loaded the directions and the start button has been pressed
* start flying the directions route.
*/
private function flyRouteIfReady():void {
if (!directionsSuccessEvent || !startButtonPressed) {
return;
}
var directions:Directions = directionsSuccessEvent.directions;
// Extract the route.
route = directions.getRoute(0);
// Draws the polyline showing the route.
polyline = directions.createPolyline();
addOverlay(directions.createPolyline());
// Creates a car marker that is moved along the route.
var car:DisplayObject = new Car();
marker = new Marker(route.startGeocode.point, new MarkerOptions({
icon: car,
iconOffset: new Point(-car.width / 2, -car.height)
}));
addOverlay(marker);
transformPolyToWorld();
createCumulativeArrays();
// Load Panoramio data for the region covered by the route.
loadPanoramioData(directions.bounds);
var duration:Number = route.duration;
// Start a timer that will trigger the car moving after the lead in time.
var leadInTimer:Timer = new Timer(LEAD_IN_DURATION * 1000, 1);
leadInTimer.addEventListener(TimerEvent.TIMER, onLeadInDone);
leadInTimer.start();
var flyTime:Number = -LEAD_IN_DURATION;
// Set up the camera flight trajectory.
for each (var flyStep:Object in FLY_TRAJECTORY) {
var time:Number = flyStep.fraction * duration;
var center:LatLng = latLngAt(time);
var scaledTime:Number = time * SCALE_TIME;
var zoom:Number = flyStep.zoom;
var attitude:Attitude = flyStep.attitude;
var elapsed:Number = scaledTime - flyTime;
flyTime = scaledTime;
flyTo(center, zoom, attitude, elapsed);
}
}
/**
* Loads Panoramio data for the route bounds. We load data about more photos
* than we need, then select a subset lying along the route.
* @param bounds Bounds within which to fetch images.
*/
private function loadPanoramioData(bounds:LatLngBounds):void {
var params:Object = {
order: "popularity",
set: "full",
from: "0",
to: NUM_GEOTAGGED_PHOTOS.toString(10),
size: "small",
minx: bounds.getWest(),
miny: bounds.getSouth(),
maxx: bounds.getEast(),
maxy: bounds.getNorth()
};
var loader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest(
"http://www.panoramio.com/map/get_panoramas.php?" +
paramsToString(params));
loader.addEventListener(Event.COMPLETE, onPanoramioDataLoaded);
loader.addEventListener(IOErrorEvent.IO_ERROR, onPanoramioDataFailed);
loader.load(request);
}
/**
* Transforms the route polyline to world coordinates.
*/
private function transformPolyToWorld():void {
var numVertices:int = polyline.getVertexCount();
worldPoly = new Array(numVertices);
for (var i:int = 0; i < numVertices; ++i) {
var vertex:LatLng = polyline.getVertex(i);
worldPoly[i] = fromLatLngToPoint(vertex, 0);
}
}
/**
* Returns the time at which the route approaches closest to the
* given point.
* @param world Point in world coordinates.
* @return Route time at which the closest approach occurs.
*/
private function getTimeOfClosestApproach(world:Point):Number {
var minDistSqr:Number = Number.MAX_VALUE;
var numVertices:int = worldPoly.length;
var x:Number = world.x;
var y:Number = world.y;
var minVertex:int = 0;
for (var i:int = 0; i < numVertices; ++i) {
var dx:Number = worldPoly[i].x - x;
var dy:Number = worldPoly[i].y - y;
var distSqr:Number = dx * dx + dy * dy;
if (distSqr < minDistSqr) {
minDistSqr = distSqr;
minVertex = i;
}
}
return cumulativeVertexDistance[minVertex];
}
/**
* Returns the array index of the first element that compares greater than
* the given value.
* @param ordered Ordered array of elements.
* @param value Value to use for comparison.
* @return Array index of the first element that compares greater than
* the given value.
*/
private function upperBound(ordered:Array, value:Number,
first:int=0, last:int=-1):int {
if (last < 0) {
last = ordered.length;
}
var count:int = last - first;
var index:int;
while (count > 0) {
var step:int = count >> 1;
index = first + step;
if (value >= ordered[index]) {
first = index + 1;
count -= step - 1;
} else {
count = step;
}
}
return first;
}
/**
* Selects up to a given number of photos approximately evenly spaced along
* the route.
* @param ordered Array of photos, each of which is an object with
* a property 'closestTime'.
* @param number Number of photos to select.
*/
private function selectEvenlySpacedPhotos(ordered:Array, number:int):Array {
var start:Number = cumulativeVertexDistance[0];
var end:Number =
cumulativeVertexDistance[cumulativeVertexDistance.length - 2];
var closestTimes:Array = [];
for each (var photo:Object in ordered) {
closestTimes.push(photo.closestTime);
}
var selectedPhotos:Array = [];
for (var i:int = 0; i < number; ++i) {
var idealTime:Number = start + ((end - start) * (i + 0.5) / number);
var index:int = upperBound(closestTimes, idealTime);
if (index < 1) {
index = 0;
} else if (index >= ordered.length) {
index = ordered.length - 1;
} else {
var errorToPrev:Number =
Math.abs(idealTime - closestTimes[index - 1]);
var errorToNext:Number = Math.abs(idealTime - closestTimes[index]);
if (errorToPrev < errorToNext) {
--index;
}
}
selectedPhotos.push(ordered[index]);
}
return selectedPhotos;
}
/**
* Handles completion of loading the Panoramio index data. Selects from the
* returned photo indices a subset of those that lie along the route and
* initiates load of each of these.
* @param event Load completion event.
*/
private function onPanoramioDataLoaded(event:Event):void {
var loader:URLLoader = event.target as URLLoader;
var decoder:JSONDecoder = new JSONDecoder(loader.data as String);
var allPhotos:Array = decoder.getValue().photos;
for each (var photo:Object in allPhotos) {
var latLng:LatLng = new LatLng(photo.latitude, photo.longitude);
photo.closestTime =
getTimeOfClosestApproach(fromLatLngToPoint(latLng, 0));
}
allPhotos.sortOn("closestTime", Array.NUMERIC);
photos = selectEvenlySpacedPhotos(allPhotos, NUM_SHOWN_PHOTOS);
for each (photo in photos) {
var photoLoader:Loader = new Loader();
// The images aren't on panoramio.com: we can't acquire pixel access
// using "new LoaderContext(true)".
photoLoader.load(
new URLRequest(photo.photo_file_url));
photo.loader = photoLoader;
// Save the loader info: we use this to find the original element when
// the load completes.
photo.loaderInfo = photoLoader.contentLoaderInfo;
photoLoader.contentLoaderInfo.addEventListener(
Event.COMPLETE, onPhotoLoaded);
}
}
/**
* Creates a MouseEvent listener function that will navigate to the given
* URL in a new window.
* @param url URL to which to navigate.
*/
private function createOnClickUrlOpener(url:String):Function {
return function(event:MouseEvent):void {
navigateToURL(new URLRequest(url));
};
}
/**
* Handles completion of loading an individual Panoramio image.
* Adds a custom marker that displays the image. Initially this is made
* invisible so that it can be faded in as needed.
* @param event Load completion event.
*/
private function onPhotoLoaded(event:Event):void {
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
// We need to find which photo element this image corresponds to.
for each (var photo:Object in photos) {
if (loaderInfo == photo.loaderInfo) {
var imageMarker:Sprite = createImageMarker(photo.loader,
photo.owner_name,
photo.owner_url);
var options:MarkerOptions = new MarkerOptions({
icon: imageMarker,
hasShadow: true,
iconAlignment: MarkerOptions.ALIGN_BOTTOM | MarkerOptions.ALIGN_LEFT
});
var latLng:LatLng = new LatLng(photo.latitude, photo.longitude);
var marker:Marker = new Marker(latLng, options);
photo.marker = marker;
addOverlay(marker);
// A hack: we add the actual image after the overlay has been added,
// which creates the shadow, so that the shadow is valid even if we
// don't have security privileges to generate the shadow from the
// image.
marker.foreground.visible = false;
marker.shadow.alpha = 0;
var imageHolder:Sprite = new Sprite();
imageHolder.addChild(photo.loader);
imageHolder.buttonMode = true;
imageHolder.addEventListener(
MouseEvent.CLICK, createOnClickUrlOpener(photo.photo_url));
imageMarker.addChild(imageHolder);
return;
}
}
trace("An image was loaded which could not be found in the photo array.");
}
/**
* Creates a custom marker showing an image.
*/
private function createImageMarker(child:DisplayObject,
ownerName:String,
ownerUrl:String):Sprite {
var content:Sprite = new Sprite();
var panoramioIcon:Bitmap = new PanoramioIcon();
var iconHolder:Sprite = new Sprite();
iconHolder.addChild(panoramioIcon);
iconHolder.buttonMode = true;
iconHolder.addEventListener(MouseEvent.CLICK, onPanoramioIconClick);
panoramioIcon.x = BORDER_L;
panoramioIcon.y = BORDER_T;
content.addChild(iconHolder);
// NOTE: we add the image as a child only after we've added the marker
// to the map. Currently the API requires this if it's to generate the
// shadow for unprivileged content.
// Shrink the image, so that it doesn't obcure too much screen space.
// Ideally, we'd subsample, but we don't have pixel level access.
child.scaleX = IMAGE_SCALE;
child.scaleY = IMAGE_SCALE;
var imageW:Number = child.width;
var imageH:Number = child.height;
child.x = BORDER_L + 30;
child.y = BORDER_T + iconHolder.height + GAP_T;
var authorField:TextField = new TextField();
authorField.autoSize = TextFieldAutoSize.LEFT;
authorField.defaultTextFormat = new TextFormat("_sans", 12);
authorField.text = "author:";
content.addChild(authorField);
authorField.x = BORDER_L;
authorField.y = BORDER_T + iconHolder.height + GAP_T + imageH + GAP_B;
var ownerField:TextField = new TextField();
ownerField.autoSize = TextFieldAutoSize.LEFT;
var textFormat:TextFormat = new TextFormat("_sans", 14, 0x0e5f9a);
ownerField.defaultTextFormat = textFormat;
ownerField.htmlText =
"<a href=\"" + ownerUrl + "\" target=\"_blank\">" +
ownerName + "</a>";
content.addChild(ownerField);
ownerField.x = BORDER_L + authorField.width;
ownerField.y = BORDER_T + iconHolder.height + GAP_T + imageH + GAP_B;
var totalW:Number =
BORDER_L + Math.max(imageW, ownerField.width + authorField.width) +
BORDER_R;
var totalH:Number =
BORDER_T + iconHolder.height +
GAP_T + imageH + GAP_B +
ownerField.height +
BORDER_B;
content.graphics.beginFill(0xffffff);
content.graphics.drawRoundRect(0, 0, totalW, totalH, 10, 10);
content.graphics.endFill();
var marker:Sprite = new Sprite();
marker.addChild(content);
content.x = 30;
content.y = 0;
marker.graphics.lineStyle();
marker.graphics.beginFill(0xff0000);
marker.graphics.drawCircle(0, totalH + 30, 3);
marker.graphics.endFill();
marker.graphics.lineStyle(2, 0xffffff);
marker.graphics.moveTo(30 + 10, totalH - 10);
marker.graphics.lineTo(0, totalH + 30);
return marker;
}
/**
* Handles click on the Panoramio icon.
*/
private function onPanoramioIconClick(event:MouseEvent):void {
navigateToURL(new URLRequest(PANORAMIO_HOME));
}
/**
* Handles failure of a Panoramio image load.
*/
private function onPanoramioDataFailed(event:IOErrorEvent):void {
trace("Load of image failed: " + event);
}
/**
* Returns a string containing cgi query parameters.
* @param Associative array mapping query parameter key to value.
* @return String containing cgi query parameters.
*/
private static function paramsToString(params:Object):String {
var result:String = "";
var separator:String = "";
for (var key:String in params) {
result += separator +
encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
separator = "&";
}
return result;
}
/**
* Called once the lead-in flight is done. Starts the car driving along
* the route and starts a timer to begin fade in of the Panoramio
* images in 1.5 seconds.
*/
private function onLeadInDone(event:Event):void {
// Set startTimer non-zero so that the car starts to move.
startTimer = getTimer();
// Start a timer that will fade in the Panoramio images.
var fadeInTimer:Timer = new Timer(1500, 1);
fadeInTimer.addEventListener(TimerEvent.TIMER, onFadeInTimer);
fadeInTimer.start();
}
/**
* Handles the fade in timer's TIMER event. Sets markerAlpha above zero
* which causes the frame enter handler to fade in the markers.
*/
private function onFadeInTimer(event:Event):void {
markerAlpha = 0.01;
}
/**
* The end time of the flight.
*/
private function get endTime():Number {
if (!cumulativeStepDuration || cumulativeStepDuration.length == 0) {
return startTimer;
}
return startTimer +
cumulativeStepDuration[cumulativeStepDuration.length - 1];
}
/**
* Creates the cumulative arrays, cumulativeStepDuration and
* cumulativeVertexDistance.
*/
private function createCumulativeArrays():void {
cumulativeStepDuration = new Array(route.numSteps + 1);
cumulativeVertexDistance = new Array(polyline.getVertexCount() + 1);
var polylineTotal:Number = 0;
var total:Number = 0;
var numVertices:int = polyline.getVertexCount();
for (var stepIndex:int = 0; stepIndex < route.numSteps; ++stepIndex) {
cumulativeStepDuration[stepIndex] = total;
total += route.getStep(stepIndex).duration;
var startVertex:int = stepIndex >= 0 ?
route.getStep(stepIndex).polylineIndex : 0;
var endVertex:int = stepIndex < (route.numSteps - 1) ?
route.getStep(stepIndex + 1).polylineIndex : numVertices;
var duration:Number = route.getStep(stepIndex).duration;
var stepVertices:int = endVertex - startVertex;
var latLng:LatLng = polyline.getVertex(startVertex);
for (var vertex:int = startVertex; vertex < endVertex; ++vertex) {
cumulativeVertexDistance[vertex] = polylineTotal;
if (vertex < numVertices - 1) {
var nextLatLng:LatLng = polyline.getVertex(vertex + 1);
polylineTotal += nextLatLng.distanceFrom(latLng);
}
latLng = nextLatLng;
}
}
cumulativeStepDuration[stepIndex] = total;
}
/**
* Opens the info window above the car icon that details the given
* step of the driving directions.
* @param stepIndex Index of the current step.
*/
private function openInfoForStep(stepIndex:int):void {
// Sets the content of the info window.
var content:String;
if (stepIndex >= route.numSteps) {
content = "<b>" + route.endGeocode.address + "</b>" +
"<br /><br />" +
route.summaryHtml;
} else {
content = "<b>" + stepIndex + ".</b> " +
route.getStep(stepIndex).descriptionHtml;
}
marker.openInfoWindow(new InfoWindowOptions({ contentHTML: content }));
}
/**
* Displays the driving directions step appropriate for the given time.
* Opens the info window showing the step instructions each time we
* progress to a new step.
* @param time Time for which to display the step.
*/
private function displayStepAt(time:Number):void {
var stepIndex:int = upperBound(cumulativeStepDuration, time) - 1;
var minStepIndex:int = 0;
var maxStepIndex:int = route.numSteps - 1;
if (stepIndex >= 0 &&
stepIndex <= maxStepIndex &&
currentStepIndex != stepIndex) {
openInfoForStep(stepIndex);
currentStepIndex = stepIndex;
}
}
/**
* Returns the LatLng at which the car should be positioned at the given
* time.
* @param time Time for which LatLng should be found.
* @return LatLng.
*/
private function latLngAt(time:Number):LatLng {
var stepIndex:int = upperBound(cumulativeStepDuration, time) - 1;
var minStepIndex:int = 0;
var maxStepIndex:int = route.numSteps - 1;
if (stepIndex < minStepIndex) {
return route.startGeocode.point;
} else if (stepIndex > maxStepIndex) {
return route.endGeocode.point;
}
var stepStart:Number = cumulativeStepDuration[stepIndex];
var stepEnd:Number = cumulativeStepDuration[stepIndex + 1];
var stepFraction:Number = (time - stepStart) / (stepEnd - stepStart);
var startVertex:int = route.getStep(stepIndex).polylineIndex;
var endVertex:int = (stepIndex + 1) < route.numSteps ?
route.getStep(stepIndex + 1).polylineIndex :
polyline.getVertexCount();
var stepVertices:int = endVertex - startVertex;
var stepLeng