Starting from the little bit of hacked up code that I cooked up a while back, I've created an extensible, interface based, adaptive caching Virtual Earth Tile Server that can be extended to use any map rendering engine. The initial release has one Tile Provider which uses the ArcGIS Server SOAP API. In the coming weeks I will be adding providers for both WMS and ArcIMS services.
Project Features:
- HttpHandler (.ashx)that responds to Tile requests from Virtual Earth
- Supports multiple layers/map services through the same handler
- ArcGIS Server Tile Provider uses the AGS SOAP API (fast!)
- Projects the data - can be used with any ArcGIS Server map service
- Extensible design allows for additional Tile Providers - i.e. ArcIMS, Image Server
- Extensible design allows for additional storage providers - i.e. Amazon S3, DBMS
- Easy to use - just edit the web.config file.
- Includes a demo web page to get you up and running quickly.
- API Documentation compiled using DocProject is at http://doc.arcdeveloper.net/tileserver (coming soon!)
- Leverages the Castle Windsor Inversion of Control Container.
- Exceptions are written into a tile image so it's easy to debug your configuration.
Design
The Virtual Earth Tile Handler uses a Virtual Earth Tile Service Manager to actually do all the work. The Tile Service Manager contains a collection of Tile Services, keyed by a name. This allows a single instance of the Tile Handler to host multiple tile services. The handler determines which Tile Service to use by extracting the service name from the url of the request. Assuming your tile handler lives in a subdirectory called "tileHandler", a request to http://myserver/tileHandler/tahoe/<vetileindex>.ashx will look for the tahoe tile service, and use it's settings to create the map.
Tile Service is simply a named container for a Tile Provider (a class which creates map image tiles) and a Storage Provider (a class which can cache the images on disk)
The logic in the system is quite simple as shown below:
How to Configure Tile Server:
There are 2 main things you need to do to configure this for your site - add the layer to your Virtual Earth map control, setup a TileService in the web.config.
Adding a Tile Layer to the Virtual Earth Map Control:
Using version 6 of the Map control this is very easy (the Default.aspx file in the attached zip file has this code in it)
First Create a bounding box that will limit the area that Virtual Earth will make tile requests. This box is for Colorado.
var bounds = [new VELatLongRectangle(new VELatLong(40,-109),new VELatLong(37,-102))];
Next, create a Tile Source Specification. You will need to specify the full Url to the handler if your Virtual Earth page is not located in the same web application. What's important to note is that the name of the TileService must be in the url - in this case it's "colorado". You will need to change this to the name of the TileService you want this layer to pull from.
var tileSourceSpec = new VETileSourceSpecification("agssoap", "./colorado/%4.ashx");
From here, it's just setting some simple tile source properties...
tileSourceSpec.NumServers = 1;
tileSourceSpec.Bounds = bounds;
tileSourceSpec.MinZoomLevel = 10;
tileSourceSpec.MaxZoomLevel = 18;
tileSourceSpec.Opacity = 0.5;
tileSourceSpec.ZIndex = 100;
Finally you want to add the layer to your map
map.AddTileLayer(tileSourceSpec, true);
Setting up a Tile Service in Web.config
A Tile Service is a container for a Tile Provider and a Storage Provider, so we are going to set up these three things, and link them together. The web.config file in the download already has these sections in it - you will just need to change a few things.
Step 1: Configure the Tile Service
Change the service name to match what you put into the VETileSourceSpecification path, as this is the linkage between the Tile Service and the Url that the request is coming from. For now, leave the other elements alone.
<!-- Colorado Tile Service-->
<component id="ColoradoVETileService"
service="ArcDeveloper.TileServer.Interfaces.IVirtualEarthTileService, ArcDeveloper.TileServer.Interfaces"
type="ArcDeveloper.TileServer.Core.VirtualEarthTileService, ArcDeveloper.TileServer.Core" >
<parameters>
<!-- Service Name - must be specified in the url parsed by the handler -->
<servicename>colorado</servicename>
<!-- The tile provider for this service is AGSColorado, with it's settings defined lower down -->
<tileProvider>${AGSColorado}</tileProvider>
<!-- The storage provide for the cache is the default FileStorage Provider, but this could be changed as needed -->
<storageProvider>${FileStorageProvider}</storageProvider>
</parameters>
</component>
The other sections specify the TileProvider and StorageProvider that this Tile Service will use. If you want to use different or differently configurated providers, just copy/paste the sections and rename them. The configuration syntax here is based on the Castle Windsor project, and you can get more information about how this works by reading Simone Busoli's articles here: Part 1, Part 2, Part 3, and Part 4.
Step 2: Specify the Map Service URL
Locate the following section in the web.config
<component id="AGSColorado"
service="ArcDeveloper.TileServer.Interfaces.ITileProvider, ArcDeveloper.TileServer.Interfaces"
type="ArcDeveloper.TileServer.ArcGIS.ArcSoapTileProvider, ArcDeveloper.TileServer.ArcGIS">
<parameters>
<!-- Make sure this Url is accessible from this web server or you'll get an exception -->
<webServiceUrl>http://YOURSERVER/arcgis/services/YOURSERVICENAME/MapServer</webServiceUrl>
</parameters>
</component>
Step 3: Change the StorageProvider location
The Storage Provider is used to cache the tiles as they are created. Thus the location you specify must have write privileges granted to the identity that the ASP.NET application runs as (ASPNET on Windows XP and NETWORK SERVICE on Windows Server 2003)
<component id="FileStorageProvider"
service="ArcDeveloper.TileServer.Interfaces.IStorageProvider, ArcDeveloper.TileServer.Interfaces"
type="ArcDeveloper.TileServer.Core.FileStorageProvider, ArcDeveloper.TileServer.Core">
<parameters>
<!-- Make sure this path exists, or you'll get an Exception instantiating the FileStorageProvider -->
<cacheFilePath>c:\tiles\</cacheFilePath>
</parameters>
</component>
That's it.
Known Issues:
Occasionally empty tiles are returned from ArcGIS Server. I am still looking into why this occurs, but since the SOAP API actually returns an image, there is no way for the code to know that it's a blank image, and thus it is still added to the cache.
Do not use labeling in the map service. This is because the images are requested at their native tile size of 256 x 256. Thus the labels will be cramped into the tile, and you will have a lot of repeated labels across your map. In the future we may build out a tool that will pre-cook a tile cache, and when doing that you can request larger images which will not have this issue. For now, just turn your labels off.
Feedback / Bugs / Issues
Please use the ArcDeveloper.net forums to ask questions or raise issues. This way everyone will benefit, and it's less one on one emailing.
Source Code:
All the source code for this project is available at the ArcDeveloper project hosted at Assembla.com. You can get the source code from http://svn2.assembla.com/svn/arcdeveloper/tileserver/trunk
If you would like to contribute to the project, use the contact link at the top of this page and I will send you an invitation to join the project team.
Download
Download ArcDeveloper.net Tile Server v0.1 (from ArcScripts). This zip file contains the source code current as of February 3,2008.
Vote!
I also entered this code in the ESRI Code Challenge . If you are an EDN subscriber or are going to the 2008 Developer Summit, and find this code useful, I'd appreciate your vote!