First some prerequisites. To do rendering in WinForms you need to override the OnPaint
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(buffer, 0, 0);
}
In it we draw a bitmap buffer onto the window. This bitmap is initialized to the window's size in the constructor.
buffer = new Bitmap(this.Width, this.Height);
Everything else is done in the Load method. First a Transform class is created. This is used to calculate from screen positions to world coordinates and back. It is initialized to a center point somewhere in the Netherlands, a resolution wide enough to show the whole of the netherlands, and the width and height of the window. The transform class is not part of BruTile itself because something similar is already part of most GIS libraries.
Transform transform = new Transform(new PointF(629816f, 6805085f), 1222.992452344f, this.Width, this.Height);
Next you need to define how the tiles on the server are stored. The tile scheme needs a lot of information. Luckily the Tms service publishes this information. In future versions of BruTile I would like to automatically parse this info so it need not be specified on the client. So here I wont go into detail about what it all means.
private ITileSchema CreateTileSchema()
{
double[] resolutions = new double[] {
156543.033900000, 78271.516950000, 39135.758475000, 19567.879237500, 9783.939618750,
4891.969809375, 2445.984904688, 1222.992452344, 611.496226172, 305.748113086,
152.874056543, 76.437028271, 38.218514136, 19.109257068, 9.554628534, 4.777314267,
2.388657133, 1.194328567, 0.597164283}; TileSchema schema = new TileSchema();
schema.Name = "OpenStreetMap";
foreach (float resolution in resolutions) schema.Resolutions.Add(resolution);
schema.OriginX = -20037508.342789;
schema.OriginY = 20037508.342789;
schema.Axis = AxisDirection.InvertedY;
schema.Extent = new Extent(-20037508.342789, -20037508.342789, 20037508.342789, 20037508.342789);
schema.Height = 256;
schema.Width = 256;
schema.Format = "png";
schema.Srs = "EPSG:900913";
return schema;
}
Now you can request which tiles you need to fill up the entire view
ITileSchema schema = CreateTileSchema();
IList<TileInfo> tiles = Tile.GetTiles(schema, transform.Extent, transform.Resolution);
Next you need to specify the RequestBuilder to use. In this case you need the RequestTms class because of the Tms protocol. As arguments you need to pass the url of the OpenStreetMap tile service and the format in which the tiles are stored.
IRequestBuilder requestBuilder = new RequestTms(new Uri("http://a.tile.openstreetmap.org"), "png");
Finally we can request all tiles from the server and render them to our buffer Bitmap.
Graphics graphics = Graphics.FromImage(buffer);
foreach (TileInfo tile in tiles)
{
Uri url = requestBuilder.GetUrl(tile);
byte[] bytes = ImageRequest.GetImageFromServer(url);
Bitmap bitmap = new Bitmap(new MemoryStream(bytes));
graphics.DrawImage(bitmap, transform.WorldToMap(tile.Extent.MinX, tile.Extent.MinY, tile.Extent.MaxX, tile.Extent.MaxY));
}
The sample code above is part of the BruTile
project on codeplex. The solution also includes a WPF project which demonstrates how to get a nice user experience with tile requests on a background thread and rendering tiles at the moment they arrive.