Wednesday, April 28, 2010

Tile schema compatibility

How easy is it to combine different tile services in one map? For instance, how easy is it to combine Google Maps aerial photos with a roads overlay from a TMS. Well this depends on two things, 1) how smart is the client, and 2) how compatible are the tile schemas. Here we look at several levels of compatibility taking the TMS specification as an example.
In TMS a tile schema is described in a TileMap. The main parameters involved are:
  • Origin: the coordinate where the first tile starts. It is the bottom left corner of tile (0, 0).
  • BoundingBox: the extent in which tiles are available. The BoudingBox's bottom left coordinate is often identical to the origin but doesn't have to be.
  • TileSets (levels): a list of the available tile levels and their resolutions.
1) The same
Okay, lets keep this short, sometimes the schema's are just the same.
2) In the same tile system
When the BoundingBox's are different but the Origin and TileSets are the same a tile in one schema will have the same x ,y and level as it's corresponding tile in the other schema. The only difference between the schema's is that some tiles can be available in one scheme but not in the other.
The two schemas can be said to be in the same tile system. If the TileSets are perceives as a Z-axis it forms a kind of three dimensional coordinate system for tiles. The origin of that space is defined by Origin.X, Origin.Y and the resolution of the first TileSet. The steps are defined by the tile width, tile height, the step size between resolutions (which can be irregular to complicate things).
3) Project to another tile system
When the tile system differs it is sometimes possible to transform a tile in one tile system to the other tile system. For instance when in one schema the Y-Axis is inverted with respect to the other it is easy to see how tile (0, 0) can be mapped to tile (0, count - 1). Such a trick is sometimes used in simple cases. I have not seen it done for more complicated relations.
4) Project to the map
When all parameters are different it is still possible to simply project the tiles to the map canvas not one tile onto another. The price is that you have to do this for every tile layer separately. This can hit performance and might consume more memory because more tile layers and possibly image containers will be needed (depending on your client).

Wednesday, April 7, 2010

Porting SharpMap to Silverlight - first attempts

Over the last week I spend some time porting open source GIS library SharpMap V1 to Silverlight. My intent was to go straight towards a working version in the easiest and quickest way and try to suppress my urge to redesign. Below is how I worked through it. It might be relevant for those who have to do a similar port.

First, I removed all the files that I didn't need. I included only those that I needed to retrieve data (shapefiles) and render a map.

System.Drawing
Silverlight has no System.Drawing (GDI), so my next goal was to move this into a separate dll while maintaining all functionality. I created an IRenderer interface in SharpMap and used the existing GDI rendering for the implementation. I put this in a separate assembly called GdiRendering.dll.

The IStyle class in SharpMap also depended on System.Drawing. I replaced all the System.Drawing classes in IStyle with implementation independent classes. These were converted to System.Drawing classes in the GdiRenderer.

For raster renderers there was no separation between data retrieval and rendering at all. So I introduced an IRasterProvider and an IRaster class. In the GdiRenderer this IRaster type was treated in the same way as the geometry types.

At that point all GDI was contained in the GdiRenderer.dll and I could remove the System.Drawing dll from SharpMap. Everything worked like at the start. There was no WPF or Silverlight yet.

System.Data
Next was System.Data. There is none in Silverlight, so no DataTable or DataRow, yet the IProvider interface heavily depends on it. I spend some time looking for a good solution here. I was expecting to find some simple grid-like data structure, but I ended up using a List of FeatureDataRow for the table and derived FeatureDataRow from Dictionary. The downside here is there is no restriction on adding columns, you could add anything you like to it. I am open for better (and simple) solutions.

System.Xml
With that settled I was about to set up a Silverlight data provider so I could start on my SilverlightRendering.dll. But which provider? It turns out that the current SharpMap data providers fall in into three categories:

  1. Raster. This would work just fine but my purpose was to implement vector rendering to SL, I had raster already working in BruTile.
  2. Vectors read from db or file. This is not allowed from the SL sandbox.
  3. Vectors retrieved from web. That's what I need but it turns out they all depend on XmlDocument which is not part of Silverlight.
I worked around this problem by using WPF rendering and a file based data provider. I created two project files for my SilverlightRendering.dll, one SL, one WPF, that share the same files. I developed my renderer using the WPF version. So far I have only compiled my SL version, I have yet to find out if it actually works.

Finally Rendering
With the WPF version of the SilverlightRendering.dll I managed to render Points, MultiPoints, LineStrings, MultiLineStrings, Polygons and MultiPolygons. I was happy to see that it is also possible to render Polygons with holes, this could have been a been a show stopper for a proper gis lib. The renderer currently ignores projections but there is a Silverlight version of proj.net on http://projnet.codeplex.com.

So far it seems there are not many complications. I am currently investigating how I could use System.Data for the file and db data providers, while having a System.Data free Silverlight version. I believe it is possible to also support WPF and WinForms without too much maintenance. Support for Windows Phone 7 should be easy.

Sunday, February 7, 2010

BruTile's async tile fetcher

This morning I had some time to play around with powerpoint. Below is a diagram depicting how BruTile's tile fetcher works. The tile fetcher runs on a worker thread and fetches tiles from the web or the disk. The idea behind its setup was that the direct communication between the UI thread and the fetcher thread would be minimized. The only direct communication is a ViewChanged call to the fetcher, telling it the user is panning or zooming and a DataChanged callback telling the UI it should render. The fetcher dumps incoming tiles into the MemoryCache, which is just a big bag of tiles. The UI renderer retrieves whichever tiles it needs.




Both the fetcher and the renderer can use all kinds of smart tricks. The fetcher can pre-fetch tiles based on its current view, or on the way the view changes over time. The renderer could search for alternative tiles (higher or lower levels) when the optimal tiles are not available. Those strategies should be tuned to support each other. For instance, in the current implementation the renderer uses higher level tiles when the optimal tiles are not available, and the fetcher pre-fetches higher level tiles to assist the renderer. But the way they play together is not specified in the interface. This loose coupling keeps things simple and flexible and the renderer never has to wait for the fetcher which results in a smooth (perceived) performance.

BruTile demo updated

A new version of Tim Ebben's Silverlight demo is up.




New features:
  • Fullscreen mode
  • Auto resize of map window
  • Bounding box zoom by using ctrl or 'bbox zoom' button.
  • Address finder using Tiny Geocoder
go have a look.

Sunday, January 31, 2010

ArcBruTile released

Last week Bert Temme released ArcBruTile 0.1.6. This is a plugin for ArcGIS which uses BruTile to show tile layers in ArcGIS. This allows for an easy way to show openstreetmap data in ArcGIS which became very relevant due to it use in the Haiti crisis.

What I like so much about ArcBruTile is that it is connecting OSS to the ESRI/Microsoft world. That was one of the purposes we had in mind when creating BruTile and this is the perfect example. An added bonus is that we can now use the projection capabilities in ArcGIS on our tile layers, something that won't be possible with our C# OSS tools soon.