Archive for April, 2008

Using Mercator Tiles in ESRI’s ArcWeb Flex API

April 28, 2008

I extended ESRI’s ArcWeb Flex API’s RasterTileLayer class which normally supports only geographic tile layouts, and made it work with Mercator.  Picture a lot of trial and error, and a few functioning yet not working tile-layouts that were plain silly. 

ESRI’s ArcWeb Flex API supports making web maps from ArcWeb data-sources and from some custom data-sources such as ArcIMS and ArcGIS Server.  A couple of code-samples infer that writing your own custom imagery provider is easy.  But I wouldn’t call it easy.  The ArcWeb Flex API is closed source, the documentation extends only to public methods/properties, and does not cover all classes in the API.

Luckily, there are ways of digging.  The Flex API ships as an SWC file — a compiled Flash/Flex library.  These files are well known to be ZIP files containing a CATALOG.XML file.  This catalog file lists all of the class names in the API along with dependency classes.  Its a more convenient way of getting to know the hidden API than blindly exploring the “import com.esri.awx…” intellisense code-hints available within Flex Builder.

Once you’ve found an interesting class, cross your fingers and pray that its implementation is obvious.  Again, code-hinting goes a long way.  Its trivial to instantiate a class and see what public methods and properties are available.  Sub-class using the ”extends” keyword and you’ll gain access to the internal and protected property and method names.  Within the Flex API, the class names, code-hinting, etc. are usually descriptive enough to get by.

When the path is less clear, you’ll need to use Flex’s debugging to find your way.  I eventually sub-classed and overrode every class method/getter/setter with a simple call to the super-class.  This allowed me to follow the logical order of method calls, and keep an eye on the Flex debug Variables at every step of the class’s lifecycle.

Did I mention trial-and-error?  I still don’t know how or why some of the code I wrote works.  To me, the math hacks are what makes this cool … we say the earth is 360×360 to make things square, but fudge the latitude to fix tile Y-positioning.


package com.esri.aws.awx.map.layers
{
	import com.esri.aws.awx.map.projection.*;
	import com.esri.aws.awx.map.layers.tiles.*;

	public class MercatorTileLayer extends RasterTileLayer
	{
		private var _mercLayout : ITileLayout;

		public function MercatorTileLayer()
		{
			m_scheme =  new ClientPowerOfTwoTilingSchemeImpl(true, 17, 256, 256, 1, 1, -180, -180, 180, 180, "");
			m_tileLayout = _mercLayout = new MercatorTileLayout(m_scheme);
		}

		override protected function commitProperties():void{
			m_schemes[dataSource] = m_scheme; // setting m_schemes[] seems to prevent m_scheme resets
			super.commitProperties(); // This still resets m_tileLayout, I can't figure a way around it
			m_tileLayout = _mercLayout; // So we'll fix m_tileLayout ourselves
        }

		override public function get supportedProjections():Array
		{
			return [ProjUtils.PROJECTION_STYLE_MERCATOR];
		}

		override protected function constructUrl(tileKey:TileKey, token:String):String{
			return "http://localhost./TileServer/" + dataSource + "/" + getZoomString( tileKey ) + ".ashx";
		}

	}
}


package com.esri.aws.awx.map.layers.tiles
{
	public class MercatorTileLayout extends TileLayoutImpl implements ITileLayout
	{
		internal static var toDegrees:Number = 180.0 / Math.PI;
		internal static var toRadians:Number = Math.PI / 180.0; 

		public function MercatorTileLayout(tilingScheme:ITilingScheme)
		{
			super(tilingScheme);
		}

		override public function determineTilesForView(list:TilesList, centerX:Number, centerY:Number, mapScale:Number, displayWidth:int, displayHeight:int):TilesList
		{
			if (centerY > 85.05113) centerY = 85.05113;
			if (centerY < -85.05113) centerY = -85.05113;

			centerY = centerY * toRadians;
			centerY = Math.log( Math.abs(Math.tan( (Math.PI/4) + (centerY/2) )) );
			centerY = centerY * toDegrees;

			return super.determineTilesForView(list, centerX, centerY, mapScale, displayWidth, displayHeight);
		}

	}
}

One final note is that this won’t get you too far using Google or Microsoft’s Mercator tiles. Flash famously has the ability to bypass cross-domain restrictions using CrossDomain.xml files. Lesser known is Flash’s lameness in using images across domains. You oddly cannot smooth/resize/alter an image loaded from another domain without a CrossDomain.xml file. The Flex API does image smoothing and resizing, so no-go for commercial imagery providers without crossdomain.xml files. Ridiculous!