SharpMapTileProvider issue

Developer
Mar 3, 2011 at 4:30 PM
Edited Mar 3, 2011 at 4:31 PM

I need some help about how to use SharpMapTileProvider.

Actually my code looks like this:

// map used by mapcontrol
var osm = new TileLayer(new OsmTileSource()) { LayerName = "OSM", };
var sql = new TileLayer(new SqlTileSource()) { LayerName = "SQL", };
var map = new Map();
map.Layers.Add(new GroupTileLayer(new[] { osm, sql, }));

SqlTileSource uses as provider a SharpMapTileProvider configured as:

var provider = new SharpMapTileProvider(CreateMap());
this.Provider = provider;
.
.
.
public Map CreateMap()
{
   var map = new Map();
   map.Layers.Add(CreateDataLayer());
   return map;
}

Note that the map object for the provider isn't the same used by the mapcontrol (this can be a mistake, I'm sure...).

When new tiles are fecthed:

1) SharmPamTileProvider is involved calling renderer.Render, 

2) MapRenderer.Render calls layer.GetFeaturesInView

3) Layer.GetFeaturesInView calls return Cache.GetFeaturesInView(box, resolution); 

Now, Cache for my Layer is empty, I suppose because no one has notified the layer that it needs to load the new features (Later.ViewChanged it's never called).

A side note: I've added also directly a Layer (i.e. a Layer from a Shapefile) to the map object of the mapcontrol  and this works well, but of course I wanna use TileLayer with TileSource mainly for performance reasons.

Coordinator
Mar 4, 2011 at 12:49 PM

The SharpMapTileLayer is currently not working. This is what I tried to say in this thread when I mentioned I broke it. The problem is that it was originally written with synchronous data retrieval in mind and it does work with  async and cashing (actually that is what you describe above).

Currently I do not have a clear plan on how to fix this. There are many options possible. Perhaps I should remove GetFeaturesInView from the layer and access the DataSource directly, so the renderer could choose between the Cache or the orginal DataSource. Perhaps the renderer should own the Cache and the layer should just have the orginal DataSource. And there are more options and many contraints. Perhaps I post more thoughts later.

Paul

Developer
Mar 4, 2011 at 12:54 PM

>actually that is what you describe above

I confirm this, thanks for the clarification.

Developer
Mar 4, 2011 at 2:03 PM

How about something like this?

public byte[] GetTile(TileInfo tileInfo)
        {
            var bytes = this.cache.Find(tileInfo.Index);
            if (bytes == null)
            {
                lock (this.syncRoot)
                {                    
                    var extent = tileInfo.Extent;
                    var box = new BoundingBox(extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
                    var map = this.factory.CreateMap();
                    map.DataChanged += (o, e) =>
                    {
                        Action action = () => this.AsyncRender(map, tileInfo);
                        var dispatcher = Application.Current != null ? Application.Current.Dispatcher : null;
                        if (dispatcher != null && !dispatcher.CheckAccess())
                             dispatcher.BeginInvoke(action);
                        else action();
                    };
                    map.ViewChanged(true, box, extent.Width / 256);
                }
            }
            return bytes;
        }

        private void AsyncRender(IMap map, TileInfo tileInfo)
        {
            var renderer = this.factory.CreateRenderer();
            var view = this.factory.CreateView();

            var extent = tileInfo.Extent;
            view.Width = 256;
            view.Height = 256;
            view.Resolution = extent.Width / 256;
            view.Center = new Point(extent.CenterX, extent.CenterY);
            renderer.Render(view, map);

            var stream = renderer.ToBitmapStream(256, 256);
            stream.Position = 0;
            var bytes = Utilities.ReadFully(stream);
            if (bytes != null)
                this.cache.Add(tileInfo.Index, bytes);
        }

Now the problem I think to have is that I actually draw inside a Map object that isn't the same of the Map used by the Map control.

I'm working on how to share the same component between control and provider