Using Mercator Tiles in ESRI’s ArcWeb Flex API
April 28, 2008I 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!