/*	JSON2XML is licensed under Creative Commons GNU LGPL License.
	License: http://creativecommons.org/licenses/LGPL/2.1/
    Version: 0.9
	Author:  Stefan Goessner/2006
	Web:     http://goessner.net/ 
*/
function json2xml(o, tab) {
   var toXml = function(v, name, ind) {
      var xml = "";
      if (v instanceof Array) {
         for (var i=0, n=v.length; i<n; i++)
            xml += ind + toXml(v[i], name, ind+"\t") + "\n";
      }
      else if (typeof(v) == "object") {
         var hasChild = false;
         xml += ind + "<" + name;
         for (var m in v) {
            if (m.charAt(0) == "@")
               xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
            else
               hasChild = true;
         }
         xml += hasChild ? ">" : "/>";
         if (hasChild) {
            for (var m in v) {
               if (m == "#text")
                  xml += v[m];
               else if (m == "#cdata")
                  xml += "<![CDATA[" + v[m] + "]]>";
               else if (m.charAt(0) != "@")
                  xml += toXml(v[m], m, ind+"\t");
            }
            xml += (xml.charAt(xml.length-1)=="\n"?ind:"") + "</" + name + ">";
         }
      }
      else {
         xml += ind + "<" + name + ">" + v.toString() +  "</" + name + ">";
      }
      return xml;
   }, xml="";
   for (var m in o)
      xml += toXml(o[m], m, "");
   return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
};
/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */


/**
 * function: isValidParam
 *  method validates that param does contain information and is not undefined or null
 */			
isValidParam = function(param)
{
	return (param != undefined && param != null);
};

/**
 * function: validateLatLonParams
 *  method validate latitude and longitude values, if values are not numeric and exception is thrown.
 */			
validateLatLonParams = function(latitude, longitude)
{
	validateNumber('Latitude', latitude);
	validateNumber('Longitude', longitude);
};

/**
 * function: validateNumber
 *  methods validates if value is numeric, if not an exception is thrown
 */			
validateNumber = function(varName, varValue)
{
	if(isNaN(varValue))
		throw new Error(varName + ' must be numeric.  ' + varName + ' = ' + varValue);
};

/**
 * function: validateZoomLevel
 *  method for validate zoom levels, current values range: 0 - 24
 */			
validateZoomLevel = function(zoomLevel)
{
	if( isNaN(zoomLevel) )
		throw new Error('Zoom Level must be numeric');	
	if( zoomLevel < 0 || zoomLevel > 24 )
		throw new Error('Zoom Level must be within 0 to 24.  Zoom Level = ' + zoomLevel);	
};

/**
 * function: addJavascript
 *  method for dynamically adding javascript.
 */			
addJavascript = function(jsname) 
{
	var th = document.getElementsByTagName('head')[0];
	var s = document.createElement('script');
	s.setAttribute('type','text/javascript');
	s.setAttribute('src',jsname);
	th.appendChild(s);
};

/**
 * function: handleInfoResponse
 *  trim white space from both the right and left hand sides of the string
 */			
trim = function(stringToTrim) 
{
	return (isValidParam(stringToTrim)) ? stringToTrim.replace(/^\s+|\s+$/g,'') : stringToTrim;
};

/**
 * function: handleInfoResponse
 *  trim white space from left hand side of the string
 */			
ltrim = function(stringToTrim) 
{
	return (isValidParam(stringToTrim)) ? stringToTrim.replace(/^\s+/,'') : stringToTrim;
};

/**
 * function: rtrim
 *  trim white space from right hand side of the string
 */			
rtrim = function(stringToTrim) 
{
	return(isValidParam(stringToTrim)) ? stringToTrim.replace(/\s+$/,'') : stringToTrim;
};

Array.prototype.inArray = function (value,caseSensitive)
// Returns true if the passed value is found in the
// array. Returns false if it is not.
{
	var i;
	for (i=0; i < this.length; i++) 
	{
		if (this[i] == value) 
		{
			return true;
		}
	}
	return false;
};

/**

   Created by: Michael Synovic;

   on: 01/12/2003

   This is a Javascript implementation of the Java Hashtable object.

	Copyright (C) 2003  Michael Synovic
	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.
	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

   Contructor(s):

    Hashtable()

             Creates a new, empty hashtable

   Method(s):

    void clear()

             Clears this hashtable so that it contains no keys.

    boolean containsKey(String key)

             Tests if the specified object is a key in this hashtable.

    boolean containsValue(Object value)

             Returns true if this Hashtable maps one or more keys to this value.

    Object get(String key)

             Returns the value to which the specified key is mapped in this hashtable.

    boolean isEmpty()

             Tests if this hashtable maps no keys to values.

    Array keys()

             Returns an array of the keys in this hashtable.

    void put(String key, Object value)

             Maps the specified key to the specified value in this hashtable. A NullPointerException is thrown is the key or value is null.

    Object remove(String key)

             Removes the key (and its corresponding value) from this hashtable. Returns the value of the key that was removed

    int size()

             Returns the number of keys in this hashtable.

    String toString()

             Returns a string representation of this Hashtable object in the form of a set of entries, enclosed in braces and separated by the ASCII characters ", " (comma and space).

    Array values()

             Returns a array view of the values contained in this Hashtable.

    Array entrySet()

             Returns a reference to the internal object that stores the data. The object is backed by the Hashtable, so changes to the Hashtable are reflected in the object, and vice-versa.

*/

function Hashtable(){

   this.hashtable = new Object();

}

/* privileged functions */

Hashtable.prototype.clear = function(){

   this.hashtable = new Object();

}              

Hashtable.prototype.containsKey = function(key){

   var exists = false;

   for (var i in this.hashtable) {

       if (i == key && this.hashtable[i] != null) {

           exists = true;

           break;

       }    

   }

   return exists;

}

Hashtable.prototype.containsValue = function(value){

   var contains = false;

   if (value != null) {

       for (var i in this.hashtable) {

           if (this.hashtable[i] == value) {

               contains = true;

               break;

           }

       }

   }        

   return contains;

}

Hashtable.prototype.get = function(key){

   return this.hashtable[key];

}

Hashtable.prototype.isEmpty = function(){

   return (parseInt(this.size()) == 0) ? true : false;

}

Hashtable.prototype.keys = function(){

   var keys = new Array();

   for (var i in this.hashtable) {

       if (this.hashtable[i] != null)

           keys.push(i);

   }

   return keys;

}

Hashtable.prototype.put = function(key, value){

   if (key == null || value == null) {

       throw "NullPointerException {" + key + "},{" + value + "}";

   }else{

       this.hashtable[key] = value;

   }

}

Hashtable.prototype.remove = function(key){

   var rtn = this.hashtable[key];

   this.hashtable[key] = null;

   return rtn;

}    

Hashtable.prototype.size = function(){

   var size = 0;

   for (var i in this.hashtable) {

       if (this.hashtable[i] != null)

           size ++;

   }

   return size;

}

Hashtable.prototype.toString = function(){

   var result = "";

   for (var i in this.hashtable)

   {    

       if (this.hashtable[i] != null)

           result += "{" + i + "},{" + this.hashtable[i] + "}\n";  

   }

   return result;

}                                  

Hashtable.prototype.values = function(){

   var values = new Array();

   for (var i in this.hashtable) {

       if (this.hashtable[i] != null)

           values.push(this.hashtable[i]);

   }

   return values;

}                                  

Hashtable.prototype.entrySet = function(){

   return this.hashtable;

}

/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */




/*
 * Class: ImageryInfo
 * Instances of ImageryInfo a simple object containing information about selected imagery.
 * Create a new ImageryInfo with the ImageryInfo constructor.
 */
 
/*
 * Constructor: ImageryInfo
 * Constructor for a new ImageryInfo instance.
 *
 * Parameters:
 * 	jsonObject - response object from ImageBuilder info call
 * 	imageUrl - url to specified image
 *
 */    
var ImageryInfo = function(jsonObject, imageUrl)
{
	var _jsonObj = null;
	var _url = '';
	var _tileIds = new Array();
	var _collectionIds = new Array();

	if(isValidParam(jsonObject))
		_jsonObj = jsonObject;
	if(isValidParam(imageUrl))
		_url = imageUrl;
		
	this._getJsonObj = function(){return _jsonObj;};
	function getCollectionId()
	{
		var tid = _jsonObj.TID;
		var colid = tid.substr(0,3);
		return colid;
	};
	
	this._getBoundingBoxImageUrl = function()
	{
		_url += '&collection=' + getCollectionId();
		return _url;
	};

	this._addTileId = function(id)
	{
		if(isValidParam(id))
		{
			_tileIds.push(id);
			var col = id.substr(0,3);
			if( !_collectionIds.inArray(col, true) )
				_collectionIds.push(col);
		}
	};
	
	this._clearTileIds = function()
	{
		_tileIds = new Array();
		_collectionIds = new Array();
	};
	
	this._getTileIds = function()
	{
		return _tileIds;
	};
	
	this._getCollectionIds = function()
	{
		return _collectionIds;
	}	
};

ImageryInfo.prototype =
{
	addTileId: function(id)
	{
		this._addTileId(id);
	},
	clearTileIds: function()
	{
		this._clearTileIds();
	},
	getTileIds: function()
	{
		return this._getTileIds();
	},
	getCollectionIds: function()
	{
		return this._getCollectionIds();
	},
	
	/*
	 * Function: toXml
	 * 	Returns XML representation of JSON Object
	 *
	 * Parameters: void
	 *
	 * Returns:
	 * 	XML representation of JSON object
	 */
	toXml: function()
	{
		var obj = this._getJsonObj();
		if( isValidParam(obj) )	
			return json2xml(obj, '    ');
		else
			return null;
	},
	
	/*
	 * Function: getAcquistionDate
	 * 	Returns the imagery acquistion date
	 *
	 * Parameters: 
	 * 	void
	 *
	 * Returns: 
	 * 	date imagery was acquired
	 */
	getAcquistionDate: function()
	{
		var obj = this._getJsonObj();
		if( isValidParam(obj) )
			return obj.DATE;
		else
			return null;
	},
	
	/*
	 * Function: getProvider
	 * 	Returns the name of the imagery provider.
	 *
	 * Parameters: 
	 * 	void
	 *
	 * Returns: 
	 * 	string name of provider of the imagery
	 */
	getProvider: function()
	{
		var obj = this._getJsonObj();
		if( isValidParam(obj) )
			return obj.PROVIDER;
		else
			return null;
	},
	
	/*
	 * Function: getResolution
	 *	Returns the images resolution.
	 *
	 * Parameters: 
	 * 	void
	 *
	 * Returns: 
	 * 	string resolution of imagery
	 */
	getResolution: function()
	{
		var obj = this._getJsonObj();
		if( isValidParam(obj) )
			return obj.RESOLUTION;
		else
			return null;
	},
	
	/*
	 * Function: getAccuracy
	 *	returns the images accuracy.
	 *
	 * Parameters: 
	 * 	void
	 *
	 * Returns: 
	 * 	string accuracy of the imagery
	 */
	getAccuracy: function()
	{
		var obj = this._getJsonObj();
		if( isValidParam(obj) )
			return obj.ACCURACY;
		else
			return null;
	},
	
	/*
	 * Function: getCollectionId
	 *	Returns the images containing collection id.
	 *
	 * Parameter: 
	 * 	void
	 *
	 * returns: 
	 * 	int collection id for the specified imagery
	 */
	getCollectionId: function()
	{
		var obj = this._getJsonObj();
		if(isValidParam(obj))
			return obj.TID.substr(0,3);
		else
			return null;
	},
	
	/* Function: getImageryUrl
	 *	Returns a url to the image.
	 *
	 * Parameter: 
	 * 	void
	 *
	 * Returns: 
	 * 	string url for the specified image
	 */
	getImageryUrl: function()
	{
		return this._getBoundingBoxImageUrl();
	},

	getTileId: function()
	{
		var obj = this._getJsonObj();
		if(isValidParam(obj))
			return obj.TID;
		else
			return null;
	}
};
/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */



/*
 * Class: AreaOfInterest
 * Instances of AreaOfInterest a simple object containing bounding box or center point/meters per pixel.
 * Create a new AreaOfInterest with the AreaOfInterest constructor.
 */
 
/*
 * Constructor: AreaOfInterest
 * Constructor for a new AreaOfInterest instance.
 *
 * Parameters:
 * options - {Object} Required X:<value>, Y:<value>.
 *
 * Examples:
 * (code)
 * var aoi_boundingBox = new AreaOfInterest({"top":1, "left": 2, "bottom":3, "right": 4});
 * // or
 * var aoi_centerPt = new AreaOfInterest({"centerX":1, "centerY":2, "metersPerPixel":1.0});
 * (end)
 */    
var AreaOfInterest = function(options){
	//private attributes
	var _top = -1;
	var _left = -1;
	var _bottom = -1;
	var _right = -1;
	var _centerX = -1;
	var _centerY = -1;
	var _mpp = -1;
	
	function _setTop(value){ _top = (isNaN(value))? -1 : parseFloat(value); }
	function _setLeft(value){ _left = (isNaN(value))? -1 : parseFloat(value); }
	function _setBottom(value){ _bottom = (isNaN(value))? -1 : parseFloat(value); }
	function _setRight(value){ _right = (isNaN(value))? -1 : parseFloat(value); }
	function _setCenterX(value){ _centerX = (isNaN(value))? -1 : parseFloat(value); }
	function _setCenterY(value){ _centerY = (isNaN(value))? -1 : parseFloat(value); }
	function _setMpp(value){ _mpp = (isNaN(value))? -1 : parseFloat(value); }
	
	this._getTop = function(){ return _top;}
	this._getLeft = function(){ return _left;}
	this._getBottom = function(){ return _bottom; }
	this._getRight = function(){ return _right; }
	this._getCenterX = function(){ return _centerX; }
	this._getCenterY = function (){ return _centerY; }
	this._getMpp = function(){ return _mpp; }
	
	if(isValidParam(options))
	{
		_setTop(options["top"]);
		_setLeft(options["left"]);
		_setBottom(options["bottom"]);
		_setRight(options["right"]);
		_setCenterX(options["centerX"]);
		_setCenterY(options["centerY"]);
		_setMpp(options["metersPerPixel"]);	
	}
	else
	{
		throw new Error("Invalid AOI parameters.");
	}
};

AreaOfInterest.prototype = {
	/*
	 * Function: getTop
	 * returns the top bounding box value
	 */	
	getTop: function(){return this._getTop();},
	
	/*
	 * Function: getLeft
	 * returns the left bounding box value
	 */	
	getLeft: function(){return this._getLeft();},
	
	/*
	 * Function: getBottom
	 * returns the bottom bounding box value
	 */	
	getBottom: function(){return this._getBottom();},
	
	/*
	 * Function: getRight
	 * returns the right bounding box value
	 */	
	getRight: function(){return this._getRight();},
	
	/*
	 * Function: getCenterX
	 * returns the X center point value
	 */	
	getCenterX: function(){return this._getCenterX();},
	
	/*
	 * Function: getCenterY
	 * returns the Y center point value
	 */	
	getCenterY: function(){return this._getCenterY();},
	
	/*
	 * Function: getMeterPerPixel
	 * returns the meters per pixel value
	 */		
	getMeterPerPixel: function(){return this._getMpp();},

	/*
	 * Function: useCenterPoint
	 * return true/false if center point should be used for AOI values
	 */
	useCenterPoint: function(){ return ((this._getCenterX() != -1) && (this._getCenterY() != -1) && (this._getMetersPerPixel() != -1)); }
};
/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */




/*
 * Class: TileIndex
 * Instances of TileIndex a simple object containing index values for use in the TileApi.
 * Create a new TileIndex with the <TileIndex> constructor.
 */
 
/**
 * Constructor: TileIndex
 * Constructor for a new TileIndex instance.
 *
 * Parameters:
 * options - {Object} Required X:<value>, Y:<value>.
 *
 * Examples:
 * (code)
 * var tileIndex = new TileIndex({"x":2, "y": 4});
 * (end)
 */    
var TileIndex = function(options)
{
	var _x = -1;
	var _y = -1;
	function _setX(value){ _x = (isNaN(value))? -1 : parseFloat(value); }
	function _setY(value){ _y = (isNaN(value))? -1 : parseFloat(value); }
	this._getX = function(){ return _x; }
	this._getY = function (){ return _y; }
	if(1 == 1) //isValidParam(options))
	{
		_setX(options["x"]);
		_setY(options["y"]);	
	}
	else
	{
		throw new Error("Tile index parameters.");
	}
};

TileIndex.prototype = {
	/*
	 * Function: getX
	 * 	returns the X index into the tile scheme
	 *
	 * Parameters: 
	 * 	void
	 *
	 * Returns: 
	 * 	the X index
	 */	
	getX: function(){return this._getX();},

	/*
	 * Function: getY
	 * 	returns the Y index into the tile scheme
	 *
	 * Parameters: 
	 * 	void
	 *
	 * Returns: 
	 * 	the Y index
	 */		
	getY: function(){return this._getY();}
};
/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */




/*
 * Class: TileApi
 * Instances of TileApi object for interfacing with the DigitalGlobe TileAPI
 * Create a new TileApi object with the TileApi constructor.
 * 
 */

/*
 * Constructor: TileApi
 * Constructor for a new TileApi instance.
 *
 * Parameters:
 * options - {Object} Required 
 *
 * Examples:
 * (code)
 *	var JIObj = new jImageConnect({'key':'********','objectName':'JIObj', 'clientId':'******', 'appId':'*****'});
 * (end)
 */
var TileApi = function(options)
{
	var _D = 0.0174532935199433;
	var _SERVER = 'http://www.globexplorer.com/tiles/';
	var _DEFAULT_PROJECTION = 'latlon';
	var _DEFAULT_LAYERSTACK = 49;
	var _key = '';
	var _projection = _DEFAULT_PROJECTION;
	var _layerStack = _DEFAULT_LAYERSTACK;
	var _errorUrl = null;
	var _timestamp = null;
	this._setTimeStamp = function(ts){	_timestamp = '';}; //(isValidParam(ts))? ts : new Date().getTime();}
	this._getTimeStamp = function(){return _timestamp;};

	this._getZoomLevels = function(){
		var zoomLevels = new Array();
		var proj = this._getProjection();
		var i;
		for(i = 0; i < 20; i++) {
			zoomLevels[i] = new Object();
			zoomLevels[i].zoomLevel = i;
			zoomLevels[i].name = "";
			switch(proj) {
				case 'latlon':
					zoomLevels[i].tileWidth = 180.0/Math.pow(2,i-1);
					zoomLevels[i].tileHeight = 180.0/Math.pow(2,i-1);
				break;
				case 'mercator-ellipsoid': 
					zoomLevels[i].tileWidth = 20037508.342789244/Math.pow(2,i-1);
					zoomLevels[i].tileHeight = 20037508.342789244/Math.pow(2,i-1);
				break;
				case 'mercator-spheroid':
					zoomLevels[i].tileWidth = 20015086.79602057/Math.pow(2,i-1);
					zoomLevels[i].tileHeight = 20015086.79602057/Math.pow(2,i-1);
				break;
			}
			zoomLevels[i].tileImageWidth = 256;
			zoomLevels[i].tileImageHeight = 256;
		}
		
		return zoomLevels;
	};

	this._latLonToIndex = function(latitude, longitude, zoomLevel)
	{	
		validateLatLonParams(latitude, longitude);
		validateZoomLevel(zoomLevel);	
		var x = ( Math.round( ((longitude + 180.0) * (Math.pow(2.0, zoomLevel))) / 360.0 ) );
		var y = ( Math.round( ((90 - latitude) * (Math.pow(2.0,zoomLevel))) / 360.0 ) );
		return new TileIndex({"x":x, "y":y});
	};

	this._mercEllToIndex = function(xm, ym, zoomLevel)
	{	
		var C = 20037508.342789244; 
		//validateLatLonParams(latitude, longitude);
		//validateZoomLevel(zoomLevel);	
		var x = Math.round(  ((xm + C) * (Math.pow(2.0, zoomLevel))) / (2*C));
		var y =  Math.round( ((C - ym) * (Math.pow(2.0,zoomLevel))) / (2*C));
		return new TileIndex({"x":x, "y":y});
	};

	this._mercSphToIndex = function(xm, ym, zoomLevel)
	{	
		var C = 20015086.79602057;
		//validateLatLonParams(latitude, longitude);
		validateZoomLevel(zoomLevel);	
		var x = ( Math.round( ((xm + C) * (Math.pow(2.0, zoomLevel))) / (2*C) ) );
		var y = ( Math.round( ((C - ym) * (Math.pow(2.0,zoomLevel))) / (2*C)) );
		return new TileIndex({"x":x, "y":y});
	};

	this._getKey = function(){ return _key; };
	this._setKey = function(key)
	{
		if(isValidParam(key))
			_key = key;
		else
			throw new Error('Key Id must be defined.');
	};	
	
	this._getLayerStack = function(){ return _layerStack; };

	this._setLayerStack = function(layerStack)
	{
		_layerStack = (isNaN(layerStack)) ? _DEFAULT_LAYERSTACK : layerStack;
	};
	
	this._getErrorUrl = function(){ return _errorUrl; };
	this._setErrorUrl = function(errorUrl)
	{
		_errorUrl = (isValidParam(errorUrl)) ? errorUrl : null;
	};
	
	this._getProjection = function(){ return _projection; };
	this._setProjection = function(projection)
	{
		_projection = (isValidParam(projection)) ? projection.toLowerCase() : _DEFAULT_PROJECTION;
	};
	
	
	this._getBaseUrl = function(imageryType)
	{
		var url = _SERVER + 'img?p=' + _projection + '&n=1&l=' + _layerStack + '&key=' + _key + '&t=' + imageryType;
		return url;
	};
	
	//initialize the private variables
	if( isValidParam(options) )
	{
		this._setLayerStack(options['layerStack']);
		this._setProjection(options['tileProjection']);
		this._setErrorUrl(options['errorUrl']);
		this._setKey(options['key']);
		this._setTimeStamp(options['timestamp']);
	}
	else
	{
		throw new Error("Could not initialize TileApi connection.");
	}
};

TileApi.prototype = 
{
	/*
	 * Function: getTileUrl
	 * 	Returns a url to an image
	 *
	 * Parameters:
	 * 	aoi - AreaOfInterest object 
	 * 	zoom - integer value for the zoom level to displayed
	 * 	imageryType - string described the type of tile to display
	 * 
	 * Returns:
	 * 	string (url) to an image to be displayed
	 *
	 * See Also:
	 *  	<AreaOfInterest>
	 */	
	getTileUrl: function(aoi, zoom, imageryType)
	{
		if(isValidParam(aoi))
		{
			validateNumber("Top Coordinate", aoi.getTop());
			validateNumber("Left Coordinate", aoi.getLeft());
			validateNumber("Bottom Coordinate", aoi.getBottom());
			validateNumber("Right Coordinate", aoi.getRight());
			if( isNaN(zoom) )
				throw new Error("Zoom scale must be defined.");
				
			var index;
			var proj = this._getProjection();
			switch(proj)
			{
				case 'mercator-ellipsoid':
					index = this._mercEllToIndex(aoi.getLeft(),aoi.getTop(),zoom);
					break;
				case 'mercator-spheroid':
					index = this._mercSphToIndex(aoi.getLeft(),aoi.getTop(),zoom);
					break;
				case 'latlon':
					index = this._latLonToIndex(aoi.getTop(), aoi.getLeft(), zoom);
				default:
					break;
			}
			var path = '&z=' + zoom + '&xi=' + index.getX() + '&yi=' + index.getY() ;		
			return this._getBaseUrl(imageryType) + path;
		}
		else
			throw new Error("AOI must be defined.");
	},

	/**
	 * Function: getProjection
	 *   Returns the specified projection information
	 *
	 * Parameters:
	 * 	void
	 *
	 * 	Returns:
	 * 	 string (projection) name [latlon, mercator]
	 */
	getProjection: function()
	{
		return this._getProjection();
	}
};
/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */



/*
 * Class: ImageBuilder
 * Instances of ImageBuilder object for interfacing with DigitalGlobe ImageBuilder
 * Create a new ImageBuilder constructor
 */

/*
 * Constructor: ImageBuilder
 * Constructor for a new ImageBuilder instance.
 *
 * Parameters:
 * options - {Object} Required 
 *
 * Examples:
 * (code)
 *	var JIObj = new jImageConnect({'objectName':'JIObj', 'GUID':'******', 'appId':'******', 'layerStack':'49', 'projection':4326});
 * (end)
 */
 var ImageBuilder = function(options)
 {
	var _name = '';
	var _aoiList = new Array();
	var _SERVER = 'http://image.globexplorer.com/gexservlets/';
	var _VIEWABLE_WINDOW = 0;
	var _INFO_WINDOW = 1;
	var _DEFAULT_LAYERSTACK = 49;
	var _DEFAULT_PROJECTION = 4326;
	var _DEFAULT_IMAGE_WIDTH = 256;
	var _DEFAULT_IMAGE_HEIGHT = 256;
	var _DEFAULT_EXTENTS = 'all';
	var _DEFAULT_ZOOM_LEVELS = [
		{"zoomLevel":0,"name":"","tileWidth":360,"tileHeight":360,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":1,"name":"","tileWidth":180,"tileHeight":180,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":2,"name":"","tileWidth":90,"tileHeight":90,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":3,"name":"","tileWidth":45,"tileHeight":45,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":4,"name":"","tileWidth":22.5,"tileHeight":22.5,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":5,"name":"","tileWidth":11.25,"tileHeight":11.25,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":6,"name":"","tileWidth":5.625,"tileHeight":5.625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":7,"name":"","tileWidth":2.8125,"tileHeight":2.8125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":8,"name":"","tileWidth":1.40625,"tileHeight":1.40625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":9,"name":"","tileWidth":0.703125,"tileHeight":0.703125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":10,"name":"","tileWidth":0.3515625,"tileHeight":0.3515625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":11,"name":"","tileWidth":0.17578125,"tileHeight":0.17578125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":12,"name":"","tileWidth":0.087890625,"tileHeight":0.087890625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":13,"name":"","tileWidth":0.0439453125,"tileHeight":0.0439453125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":14,"name":"","tileWidth":0.02197265625,"tileHeight":0.02197265625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":15,"name":"","tileWidth":0.010986328125,"tileHeight":0.010986328125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":16,"name":"","tileWidth":0.0054931640625,"tileHeight":0.0054931640625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":17,"name":"","tileWidth":0.00274658203125,"tileHeight":0.00274658203125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":18,"name":"","tileWidth":0.001373291015625,"tileHeight":0.001373291015625,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":19,"name":"","tileWidth":0.0006866455078125,"tileHeight":0.0006866455078125,"tileImageWidth":256,"tileImageHeight":256},
		{"zoomLevel":20,"name":"","tileWidth":0.00034332275390625,"tileHeight":0.00034332275390625,"tileImageWidth":256,"tileImageHeight":256}
	 ];
	var _tileWidth = '';
	var _tileHeight = '';
	var _projection = '';
	var _appId = '';
	var _GUID = '';
	var _userId = '';
	var _layerStack = '';
	var _extents = '';
	var _infolist = null;
	var _icallb = null;
 	var _zoomLevels = _DEFAULT_ZOOM_LEVELS;

	var _timestamp = '';
	this._setTimeStamp = function(ts){	_timestamp = '';}; //(isValidParam(ts))? ts : new Date().getTime();}
	this._getTimeStamp = function(){return _timestamp;}	;
	
	
	this._buildImageURL = function(aoi)
	{
		var url = (aoi.useCenterPoint()) ? _getCenterPointImageUrl(aoi) : _getBoundingBoxImageUrl(aoi);
		return url;
	};
	
	this._buildInfoURL = function(aoi)
	{	
		var url = (aoi.useCenterPoint()) ? _getCenterPointMetadataUrl(aoi) : _getBoundingBoxMetadataUrl(aoi);
		return url;
	};	
		

	this._updateImageList = function(aoi)
	{
		var tmp = document.createElement('script');
		infolist = null;
		tmp.setAttribute('src', this._buildInfoURL(aoi));
		tmp.setAttribute('type', 'text/javascript');
		tmp.setAttribute('id', _newCallId());
		var head = document.getElementsByTagName('head')[0];
		head.appendChild(tmp);
	};
	
	function _buildUserInfoUrl()
	{
		var url = 'id=' + _GUID + '&uid=' +_userId + '&appid=' + _appId + '&ls=' + _layerStack;
		return url;
	};

	this._buildUserInfoUrl = function()
	{
		return _buildUserInfoUrl();
	};

	function _getCenterPointMetadataUrl(aoi)
	{
		var url = _SERVER + 'meta?cmd=info&' + _buildUserInfoUrl();
		url += '&xc=' + aoi.getCenterX() + '&yc=' + aoi.getCenterY() + '&mpp=' + aoi.getMetersPerPixel() + '&iw=' + _tileWidth + '&ih=' + _tileHeight;
		url += '&projid=' + _projection + '&extentset=' + _extents + '&responsecontenttype=json&callback=' + _name + '.handleInfoResponse';
		url += '&ts='+_timestamp;
		return url;
	};
	
	function _getCenterPointImageUrl(aoi)
	{
		var url = _SERVER + 'gex?cmd=image&' + _buildUserInfoUrl();
		url += '&xc=' +aoi.getCenterX() + '&yc=' + aoi.getCenterY() + '&mpp=' + aoi.getMetersPerPixel() + '&iw=' + _tileWidth + '&ih=' + _tileHeight;
		url += '&projid=' + _projection + '&extentset=' + _extents + '&ts='+_timestamp;
		return url;
	};
	
	function _getBoundingBoxMetadataUrl(aoi)
	{
		var url = _SERVER + 'meta?cmd=info&' + _buildUserInfoUrl();
		url += '&xul=' + aoi.getLeft() + '&yul=' + aoi.getTop() + '&xlr=' + aoi.getRight() +'&ylr=' + aoi.getBottom() +'&iw=' + _tileWidth + '&ih=' + _tileHeight;
		url += '&projid=' + _projection + '&extentset=' + _extents + '&responsecontenttype=json&callback=' + _name + '.handleInfoResponse';		
		url += '&ts='+_timestamp;
		return url;
	};

	
	function _getBoundingBoxImageUrl(aoi)
	{
		var url = _SERVER + 'gex?cmd=image&' + _buildUserInfoUrl();
		url += '&xul=' + aoi.getLeft() + '&yul=' + aoi.getTop() + '&xlr=' + aoi.getRight() +'&ylr=' + aoi.getBottom() +'&iw=' + _tileWidth + '&ih=' + _tileHeight;
		url += '&projid=' + _projection + '&extentset=' + _extents + '&ts='+_timestamp;
		return url;
	};
	
	function _newCallId(){ return('test'); };
	
	this._setApplicationId = function(appId)
	{
		if(isValidParam(appId))
			_appId = appId;
		else
			throw new Error('Application Id must be defined.');
			
	};

	this._setClientId = function(GUID)
	{
		if(isValidParam(GUID))
			_GUID = GUID;
		else
			throw new Error("Client Id must be defined.");
	};
	
	this._setUserId = function(userId)
	{
		if(isValidParam(userId))	
			this._userId = userId;
		else
			throw new Error("User Id must be defined.");
	};
	
	this._setLayerStack = function(layerStack)
	{
		_layerStack = (isNaN(layerStack)) ? _DEFAULT_LAYERSTACK : layerStack;
	};
	
	this._setProjection = function(projection)
	{
		_projection = (isValidParam(projection)) ? projection : _DEFAULT_PROJECTION;
	};
	
	this._setName = function(name)
	{
		if( isValidParam(name) )
			_name = name;
		else
			throw new Error('Object name must be defined.');
	};
	
	this._setExtents = function(extents)
	{ 
		_extents = ( isValidParam(extents) )? extents : _DEFAULT_EXTENTS; 
	};
	
	this._setViewableAOI = function(aoi)
	{
		if(isValidParam(aoi))
			_aoiList[_VIEWABLE_WINDOW] = aoi;
	};

	this._setInfoAOI = function(aoi)
	{
		if(isValidParam(aoi))
			_aoiList[_INFO_WINDOW] = aoi;	
	};
	
	this._setTileWidth = function(width)
	{ 
		_tileWidth = (isNaN(width))? _DEFAULT_IMAGE_WIDTH : width;
	};
	
	this._setTileHeight = function(height)
	{ 
		_tileHeight = (isNaN(height))? _DEFAULT_IMAGE_HEIGHT : height; 
	};
	
	this._setInfoCallback = function(callback)
	{
		if( isValidParam(callback) )
			_icallb = callback;
	};

	this._setZoomLevels = function(levels)
	{
		_zoomLevels = (isValidParam(levels))? levels : _DEFUALT_ZOOM_LEVELS;
	}	


	
	this._getExtents = function(){return _extents;}
	this._getInfoAOI = function(){return _aoiList[_INFO_WINDOW];};
	this._getViewableAOI = function(){return _aoiList[_VIEWABLE_WINDOW];};
	this._getDefaultImageHeight = function(){return _DEFAULT_IMAGE_HEIGHT;};
	this._getDefaultImageWidth = function(){return _DEFAULT_IMAGE_WIDTH;};
	this._getServer = function(){return _SERVER;};
	this._getProjection = function(){ return _projection; };
	this._getLayerStack = function(){ return _layerStack; };
	this._getUserId = function(){ return _userId; };
	this._getClientId = function(){ return _GUID; };
	this._getApplicationId = function(){ return _appId; };
	this._getTileWidth = function(){return _tileWidth;};
	this._getTileHeight = function(){return _tileHeight;};
	this._getInfoCallback = function(){return _icallb;};
	this._getZoomLevels = function(){return _zoomLevels;};


	//initialize the private variables
	if( isValidParam(options) )
	{
		this._setName(options['objectName']);
		this._setLayerStack(options['layerStack']);
		this._setClientId(options['GUID']);
		this._setApplicationId(options['appId']);
		this._setProjection(options['projection']);	
		this._setExtents(options['extents']);
		this._setTimeStamp(options['timestamp']);

		//empty placeholders for viewable and info AOI's
		this._aoiList = new Array();	
		this._aoiList.push(new AreaOfInterest({'x':-1, 'y':-1}));
		this._aoiList.push(new AreaOfInterest({'x':-1, 'y':-1}));
	};
	
};

ImageBuilder.prototype = 
{		
	/*
	 * Function: setViewableWindow
	 * 	Define the viewable window currently displayed.
	 *
	 * Parameters:
	 *	aoi - AreaOfInterest object
	 *	tileWidth - integeer value defining width of viewable area
	 *	tileHeight - integer value defining height of viewable area
	 *
	 * Returns: 
	 * 	void
	 * 
	 * See Also:
	 *  	<AreaOfInterest>
	 */
	setViewableWindow: function(aoi, tileWidth, tileHeight) 
	{
		this._setViewableAOI(aoi);
		this._setTileWidth(tileWidth);
		this._setTileHeight(tileHeight);
	},

	/*
	 * Function: setInfoWindow
	 *	Define the metadata window.
	 *
	 * Parameters:
	 * 	aoi - AreaOfInterest object
	 * 	tileWidth - integer value defining width of metadata window
	 * 	tileHeight - integer value defining height of metadata window
	 *
	 * Returns:
	 * 	void
	 *
	 * See Also:
	 *  	<AreaOfInterest>
	 */
	setInfoWindow: function(aoi, tileWidth, tileHeight) 
	{
		this._setInfoAOI(aoi);
		this._setTileWidth(tileWidth);
		this._setTileHeight(tileHeight);
		this._updateImageList(aoi);
	},

	/*
	 * Function: setInfoCallback
	 *	Register callback to be executed when DigitalGlobe online platform completes execution
	 *
	 * Parameters:
	 * 	callback - function to execute when DigitalGlobe online platform execution completes
	 *
	 * Returns:
	 * 	void
	 */
	setInfoCallback: function(callback) 
	{
		if( isValidParam(callback) )
			this._setInfoCallback(callback);
	},
	
	/*
	 * Function: handleInfoResponse
	 *	Register function for handling completion of info request from DigitalGlobe online platform.
	 *
	 * Parameters:
	 *	responseHandler - function to execute when an info call is excuted upon completion
	 *
	 * Returns:
	 * 	void
	 */
	handleInfoResponse: function (a)  
	{
		function ord(a,b) {if(a.DATE < b.DATE) { return 1 } else if(a.DATE >b.DATE) { return -1} else {return 0}};
		var tmp_infolist = a.infoList;
		tmp_infolist.sort(ord);
		var infolist = new Array();
		var c =0;
		var i;
		var hash = new Hashtable();
		for(i = 0 ; i< tmp_infolist.length; i++) 
		{
			var srcCollectionId = tmp_infolist[i].TID.substr(0,3);
			var key = srcCollectionId + '-' + tmp_infolist[i].DATE;
			if( hash.containsKey(key) )
			{
				var imgInfo = hash.get(key);
				imgInfo.addTileId(tmp_infolist[i].TID +'');
				hash.remove(key);
				hash.put(key, imgInfo);
			}
			else
			{
				var imgInfo = new ImageryInfo(tmp_infolist[i], this._buildImageURL(this._getInfoAOI()));
				imgInfo.addTileId(imgInfo.getTileId());
				hash.put(key, imgInfo);
			}
		}
		var cb = this._getInfoCallback();
		if(cb) 
		{
			cb(hash.values());
		}
	},

	
	/*
	 * Function: getUrl
	 *	Return url to imagery as defined using the setViewableWindow method
	 *
	 * Parameters:
	 * 	void
	 *
	 * Returns:
	 * 	string - url to image returned by the DigitalGlobe online platform
	 */	
	getUrl: function() 
	{
		return(this._buildImageURL(this._getViewableAOI()));
	},	
	
	/*
	 * Function: getMetadata
	 *	Return url to metadata about the window defined using the setInfoWindow method
	 *
	 * Parameters:
	 * 	void
	 *
	 * Returns:
	 * 	string - url to metadata returned by the DigitalGlobe online platform
	 */
	getMetadata: function()
	{
		return(this._buildInfoURL(this._getInfoAOI()));
	},

		
	/*
	 * Function: getUrlBounds
	 *	Return bounding box information
	 *
	 * Parameters:
	 * 	left - decimal, longitude value describing bounding box
	 * 	top - decimal, latitiude value describing bounding box
	 * 	right - decimal, longitude value describing bounding box
	 * 	bottom - decimal, latitude value describing bounding box
	 *
	 * Returns:
	 * 	void
	 */
	getUrlBounds: function(left, top, right, bottom, width, height) 
	{
		var url = this._getServer() + 'gex?' + this._buildUserInfoUrl() + '&cmd=image';
		url += "&xul=" + left + "&yul=" + top + "&xlr=" + right + "&ylr=" + bottom + "&iw=" + width+ "&ih=" + height;
		url += '&projid=' + this._getProjection() + '&extentset=' + this._getExtents() + '&ts=' + this._getTimeStamp();
		return(url);
	},

	/**
	 * Function: getProjection
	 *   Returns the specified projection information
	 *
	 * Parameters:
	 * 	void
	 *
	 * 	Returns:
	 * 	 string (projection) ESPG id
	 */	
	getProjection: function()
	{
		return this._getProjection();
	}
	
};
/*
 *-------------------------------------------------------------------
 * (C) Copyright 2001-2009 All Rights Reserved
 *
 * The information and design as detailed in this document is the
 * property of DigitalGlobe Inc., and/or their Associates, and 
 * must be returned on demand. It is issued on the strict condition 
 * that except with explicit written permission it must not be reproduced,
 * copied or communicated in part or in whole to any third party, nor 
 * be used for any purpose other than that stated in the particular 
 * enquiry, order or contract with which it is issued. 
 *
 *
 * Unless otherwise noted, DigitalGlobe Inc. is the sole owner of all intellectual 
 * property contained in or related to this document, and shall retain all such rights
 * and privileges thus entitled to it at all times.
 *
 * 
 * The reservation of copyright in this document extends from each date
 * appearing thereon and in respect of the subject matter as it appeared
 * at the relevant date.
 *--------------------------------------------------------------------
 */


/*
 * Class: jImageConnect
 * A class that allows imagery to be displayed in web based mapping applications via 
 * the DigitalGlobe online platform.
 */

/*
 * Constructor: jImageConnect
 * Constructor for a new jImageConnect instance.
 *
 * Parameters:
 * options - {Object} Required
 *   objectName : [name of javascript object] - default = null
 *	 GUID : [ImageBuilder GUID ] - default = null
 *   appId : [ImageBuilder application id] - default = null
 *   key : [TileApi key] - default = null
 *   layerStack : [ImageBuilder layerstack] - default = 49
 *   projection : [EPSG ID] - default = 4326 
 *	 tileProjection : [latlon, mercator-ellipsoid, mercator-spheroid] - default = latlon
 *	 errorUrl : [url for error image] - default = null
 *   extents : [visible, hidden, all] - default = all   
 *
 * Examples:
 * (code)
 *	var JIObj = new jImageConnect({'objectName':'JIObj', 'GUID':'******************', 'appId':'******', 'layerStack':'49', 'projection':4326});
 * 	// or
 *	var JIObj = new jImageConnect({'key':'********','objectName':'JIObj', 'GUID':'******', 'appId':'*****'});
 * (end)
 */
var jImageConnect = function(options)
{
	var _VERSION = 0.1;
	var _timeStamp = new Date().getTime();
	var _imageBuilderObj = null;
	var _tileApiObj = null;
	var _tileIds = '';
	var _collectionIds = '';
	if( isValidParam(options) )
	{
		options['timestamp'] = _timeStamp;
		if( isValidParam(options['key']) )
			_tileApiObj = new TileApi(options);
		if( isValidParam(options['GUID']) && isValidParam(options['appId']) && isValidParam(options['objectName']) )
			_imageBuilderObj = new ImageBuilder(options);
		if( !isValidParam(_imageBuilderObj) && !isValidParam(_tileApiObj))
			throw new Error('Invalid jImageConnect setup.  Please check creation options.');
	}
	
	this._getVersion = function(){return _VERSION;};
	this._getImageBuilderObj = function(){ return _imageBuilderObj; };
	this._getTileApiObj = function(){ return _tileApiObj; };
	this._getTileIds = function() {return _tileIds;};
	this._getCollectionIds = function(){return _collectionIds;};
	this._addTileIds = function(ids){ _tileIds += ids; };
	this._addCollectionIds = function(ids){_collectionIds += ids;};
	this._clearIds = function()
	{
		_tileIds = '';
		_collectionIds = '';
	}
	this._mercator2LatLon = function(aoi)
	{
		var left = (aoi.getLeft()/20037508.34)*180;

		var right = (aoi.getRight()/20037508.34)*180;
	
		var lat=(aoi.getTop()/20037508.34)*180;
		lat=180/Math.PI*(2*Math.atan(Math.exp(lat*Math.PI/180))-Math.PI/2);
		var top = lat;
	
		lat=(aoi.getBottom()/20037508.34)*180;
		lat=180/Math.PI*(2*Math.atan(Math.exp(lat*Math.PI/180))-Math.PI/2);
		var bottom = lat;

		return new AreaOfInterest({'top':top, 'left':left, 'bottom':bottom, 'right':right});;
	};
};

jImageConnect.prototype = 
{
	/**
	 * Function: createDynamicImage
	 *   manages imagery ids so that "non-default" imagery can be selected and viewed
	 *   imagery ids are stacked for viewability top to bottom
	 *   For example: CitySphere (top) LandSat (bottom)
	 *
	 * Parameters:
	 *  items - ImageryInfo object
	 * 
	 * Returns:
	 * 	void
	 *
	 * See Also:
	 *  <ImageryInfo>
	 */
	createDynamicImage: function(item)
	{
		this._addTileIds(item.getTileIds()+',');
		this._addCollectionIds(item.getCollectionIds()+',');
	},

	/**
	 * Function: clearDynamicImages
	 *  removes any ids system is currently managing
	 *
	 * Parameters:
	 *   void
	 *
	 * Returns:
	 *  void
	 */
	clearDynamicImages: function()
	{
		this._clearIds();
	},


	/**
	 * Function: getProjection
	 *  return ESPG id for the specified projection
	 *
	 * Parameters:
	 * 	void
	 * 
	 * Returns:
	 * 	string ESPG id
	 */	
	getProjection: function()
	{
		var obj = this._getImageBuilderObj();
		return (isValidParam(obj)) ? obj.getProjection() : null;
	},

	/**
	 * Function: getProjection
	 *   Returns the specified projection information
	 *
	 * Parameters:
	 * 	void
	 *
	 * 	Returns:
	 * 	 string (projection) name [latlon, mercator-ellipsoid, mercator-spheroid]
	 */
	getTileProjection: function()
	{
		var obj = this._getTileApiObj();
		return (isValidParam(obj)) ? obj.getProjection() : null;
	},

	/*
	 * Function: getTileUrl
	 * 	Returns a url to an image
	 *
	 * Parameters:
	 * 	aoi - AreaOfInterest object 
	 * 	zoom - integer value for the zoom level to displayed
	 * 	imageryType - string described the type of tile to display
	 * 
	 * Returns:
	 * 	string (url) to an image to be displayed
	 *
	 * See Also:
	 *  	<AreaOfInterest>
	 */
	getTileUrl: function(aoi, zoom, imageryType)
	{
		var ti = this._getTileApiObj();
		var ib = this._getImageBuilderObj();
		if(isValidParam(ti))
		{
			var param = this._getCollectionIds();
			var hasParams = (trim(param).length > 0 );
			var url = ti.getTileUrl(aoi, zoom, imageryType);
			url +=  (hasParams) ? "&coll=" + param : "";
			return url;
		}
		else if(isValidParam(ib))
		{
			ib.setViewableWindow(aoi,256,256 );
			var param = this._getTileIds();
			var hasParams = (trim(param).length > 0);
			var url = ib.getUrl() + (hasParams) ? "&tid=" + param : "";
			return url;
		}
		else
		{
			return null;
		}
	},
	
	/*
	 * Function: setViewableWindow
	 * 	Define the viewable window currently displayed.
	 *
	 * Parameters:
	 *	aoi - AreaOfInterest object
	 *	tileWidth - integeer value defining width of viewable area
	 *	tileHeight - integer value defining height of viewable area
	 *
	 * Returns: 
	 * 	void
	 * 
	 * See Also:
	 *  	<AreaOfInterest>
	 */
	setViewableWindow: function(aoi, tileWidth, tileHeight) 
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
			return ib.setViewableWindow(aoi, tileWidth, tileHeight);
		else
			return null;
	},

	

	/*
	 * Function: setInfoWindow
	 *	Define the metadata window.
	 *
	 * Parameters:
	 * 	aoi - AreaOfInterest object
	 * 	tileWidth - integer value defining width of metadata window
	 * 	tileHeight - integer value defining height of metadata window
	 *
	 * Returns:
	 * 	void
	 *
	 * See Also:
	 *  	<AreaOfInterest>
	 */
	setInfoWindow: function(aoi, tileWidth, tileHeight)
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
		{
			if( this.getTileProjection().indexOf('mercator') > -1 )
				aoi = this._mercator2LatLon(aoi);
			
			return ib.setInfoWindow(aoi, tileWidth, tileHeight);
		}
		else
			return null;	
	},
	
	/*
	 * Function: setInfoCallback
	 *	Register callback to be executed when DigitalGlobe online platform completes execution
	 *
	 * Parameters:
	 * 	callback - function to execute when DigitalGlobe online platform execution completes
	 *
	 * Returns:
	 * 	void
	 */
	setInfoCallback: function( callback ) 
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
			return ib.setInfoCallback(callback)
		else
			return null;	
	},
	
	/*
	 * Function: handleInfoResponse
	 *	Register function for handling completion of info request from DigitalGlobe online platform.
	 *
	 * Parameters:
	 *	responseHandler - function to execute when an info call is excuted upon completion
	 *
	 * Returns:
	 * 	void
	 */
	handleInfoResponse: function ( responseHandler )
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
			return ib.handleInfoResponse( responseHandler )
		else
			return null;	
	},
	

	/*
	 * Function: getUrl
	 *	Return url to imagery as defined using the setViewableWindow method
	 *
	 * Parameters:
	 * 	void
	 *
	 * Returns:
	 * 	string - url to image returned by the DigitalGlobe online platform
	 */	
	getUrl: function()
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
		{
			var param = this._getTileIds();
			var hasParams = (trim(param).length > 0);
			var aa = ib.getUrl();
			var bb = (hasParams) ? "&tid=" + param : "";
			var url = aa + bb;
			return url;
		}
		else
			return null;	
	},
	
	/*
	 * Function: getMetadata
	 *	Return url to metadata about the window defined using the setInfoWindow method
	 *
	 * Parameters:
	 * 	void
	 *
	 * Returns:
	 * 	string - url to metadata returned by the DigitalGlobe online platform
	 */
	getMetadata: function()
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
			return ib.getMetadata();
		else
			return null;	
	},

	
	/*
	 * Function: getUrlBounds
	 *	Return bounding box information
	 *
	 * Parameters:
	 * 	left - decimal, longitude value describing bounding box
	 * 	top - decimal, latitiude value describing bounding box
	 * 	right - decimal, longitude value describing bounding box
	 * 	bottom - decimal, latitude value describing bounding box
	 *
	 * Returns:
	 * 	void
	 */
	getUrlBounds: function(left, top, right, bottom, width, height)
	{
		var ib = this._getImageBuilderObj();
		if(isValidParam(ib))
		{
			var param = this._getTileIds();
			var hasParams = (trim(param).length > 0);
			var url = ib.getUrlBounds(left, top, right, bottom, width, height);
			url += (hasParams) ? "&tid=" + param : "";
			return url;
		}
		else
			return null;	
	},

	/*
	 * Function: getZoomLevels
	 *	Return zoom level definintion
	 *
	 * Parameters:
	 * 	void
	 *
	 * Returns:
	 * 	Array of zoom level definitions
	 */	
	getZoomLevels: function()
	{
		var ti = this._getTileApiObj();
		var ib = this._getImageBuilderObj();
        if(isValidParam(ti)) 
			return ti._getZoomLevels();
		else if(isValidParam(ib)) 
			return ib._getZoomLevels(); 
		else 
			return null;
	},

	/*
	 * Function: setZoomLevels
	 * 	Set defined zoom levels 
	 *
	 * Parameters:
	 * 	levels - Array of zoom level definitions
	 *
	 * Example:
	 * 	
	 *
	 * Returns:
	 * 	void
	 *
	 */
	setZoomLevels: function(levels)
	{
		var ib = this._getImageBuilderObj();	
		if( isValidParam(ib) && isValidParam(levels) )
			ib._setZoomLevel(levels);
	},

	VERSION: function()
	{
		return this._getVersion();
	}
};
