The central point in the Bing Maps (or Google Maps or OpenStreetMap) tiled map scheme is that each map tile is identified by three parameters. These are the zoom level (that typically ranges from 0 or 1 to about 20) and the x and y index of a tile within a zoom level. In a given zoom level z the x and y index range from 0 to 2^z-1. In zoom level 0 there is one tile, in level 1 there are 2x2 tile, in level 2 there are 4x4 tiles and so on.
Most map tile providers like OpenStreetMap or Google Maps directly reflect these three parameters in their tile URIs. OpenStreetMap for example provides map tiles by the URI http://tile.openstreetmap.org/z/x/y.png.
In a derived TileSource class you override the GetUri method to provide an URI for the three tile parameters. For OpenStreetMap alike tiles such a derived TileSource might look like this:
public class MyTileSource : Microsoft.Maps.MapControl.WPF.TileSource
{
public override Uri GetUri(int x, int y, int zoomLevel)
{
return new Uri(UriFormat.
Replace("{x}", x.ToString()).
Replace("{y}", y.ToString()).
Replace("{z}", zoomLevel.ToString()));
}
}
For some stupid technical detail in the Bing Maps WPF Control TileLayer class you would also have to derive your own TileLayer class to enable usage in XAML:
public class MyTileLayer : Microsoft.Maps.MapControl.WPF.MapTileLayer
{
public MyTileLayer()
{
TileSource = new MyTileSource();
}
public string UriFormat
{
get { return TileSource.UriFormat; }
set { TileSource.UriFormat = value; }
}
}
You would then use it in the Map Control like below where the XAML namespace m
references Microsoft.Maps.MapControl.WPF
and local
references the namespace that contains the derived TileLayer.
<m:Map>
<m:Map.Mode>
<!-- set empty map mode, i.e. remove default map layer -->
<m:MercatorMode/>
</m:Map.Mode>
<local:MyTileLayer UriFormat="http://tile.openstreetmap.org/{z}/{x}/{y}.png"/>
</m:Map>
Instead of creating a http URI you might now also create an URI for a local file. You could for example organize the map tiles in a directory structure with a directory for the zoom level, a subdirectory for the x index and a file name for the y index. You could set the UriFormat
property in a way that specifies a local path:
<local:MyTileLayer UriFormat="file:///C:/Tiles/{z}/{x}/{y}.png"/>
The overridden GetUri method might also directly create an appropriate local file URI, without using the UriFormat
property:
public override Uri GetUri(int x, int y, int zoomLevel)
{
string rootDir = ...
string path = Path.Combine(rootDir, zoomLevel.ToString(), x.ToString(), y.ToString());
return new Uri(path);
}
You may want to read more about how OpenStreetMap handles map tile names.