ThinkGeo.com    |     Documentation    |     Premium Support

Custom Caching Logic

Greetings,


I have the following situation:


Layer goes to the web (potentially multiple sources) and draws the results on a layer.  If all web requests complete without error, I would like to cache the tile so that next time no web requests are necessary.  If an error occurs in a single request, I would like to display the layer as it is drawn, but don't want to cache the tile.  On all further views of the tile, the cached tile is used if it exists.


Currently, I use a LayerOverlay and just set the TileCache directory.  This is fine unless there is an error with one of the web requests... it caches the layer which may be incomplete (due to error connecting to source).  I feel like the caching logic needs to be in the layer itself since it knows if there was an error.


Anyhow, I wanted to make sure I was on the right track and to see if you had any example of doing this?


Thanks,


.Ryan.



Hi Ryan, 
  
 I think that’s hard for implement your requirement. 
  
 The tile cache logic is in the LayerOverlay, but the loading source from web logic is in the layer. 
  
 When we write cache logic in LayerOverlay, we don’t know which type layer will be added in it, so we also cannot handle the special situation for web loaded layer about tile haven’t been load succeed. 
  
 In fact I cannot think about a workaround for this situation now. Maybe you can find which layer will miss tile first then add it to a special overlay and don’t set cache for it, so this can improve the performance for the entire application. 
  
 Regards, 
  
 Don

Hi Ryan, 
  
 I think that’s hard for implement your requirement. 
  
 The tile cache logic is in the LayerOverlay, but the loading source from web logic is in the layer. 
  
 When we write cache logic in LayerOverlay, we don’t know which type layer will be added in it, so we also cannot handle the special situation for web loaded layer about tile haven’t been load succeed. 
  
 In fact I cannot think about a workaround for this situation now. Maybe you can find which layer will miss tile first then add it to a special overlay and don’t set cache for it, so this can improve the performance for the entire application. 
  
 Regards, 
  
 Don

Hi Don,


If it helps, I only use 1 layer in the overlay...  so for me it would be fine if I just moved all caching/drawing logic to the layer (if possible)


 


.Ryan.



So I’ve worked on implementing this myself… I have the following code, however when I run it, c.Row and c.Column are both 0 for some reason when I’m trying to GetTile().  Note that my BuildTile() method correctly saves the tile to my hard disk. 
  
 Any help would be appreciated, thanks. 
  
 .Ryan. 
  
 
        protected override void DrawCore(GeoCanvas canvas, System.Collections.ObjectModel.Collection<SimpleCandidate> labelsInAllLayers)
        {
            FileBitmapTileCache tileCache = new FileBitmapTileCache(StaticData.GISSettings.TileCacheDirectory, “NewImagery”);
            MapSuiteTileMatrix tm = new MapSuiteTileMatrix(canvas.CurrentScale, 512, 512, GeographyUnit.DecimalDegree, canvas.CurrentWorldExtent);
            
            Collection<TileMatrixCell> cells = tm.GetIntersectingCells(canvas.CurrentWorldExtent);

            foreach (TileMatrixCell c in cells)
            {
                BitmapTile bt = tileCache.GetTile(c.Row, c.Column);
                if (bt.Bitmap == null)
                {
                    BuildTile(c, canvas, tileCache);
                    bt = tileCache.GetTile(c.Row, c.Column);
                }

                if (bt.Bitmap != null)
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        bt.Bitmap.Save(ms, ImageFormat.Bmp);
                        canvas.DrawWorldImageWithoutScaling(new GeoImage(ms), bt.BoundingBox.GetCenterPoint().X, bt.BoundingBox.GetCenterPoint().Y, DrawingLevel.LevelFour);
                    }
                }
            }
        }
 


Ryan, 
  
 Your code is incorrect, you couldn’t change the boundingBox of MapSuiteTileMatrix each time, you should keep it the same, and it represent the boundary of the whole map, for example, if your map is decimal degree, the boundingbox you can choose -180,90,180,-90, not the canvas.CurrentWorldExtent 
  
 Let me know if you still have problem. 
  
 Thanks, 
 James

Thanks James, 
  
 That gives me the correct rows/columns now.  Now my problem is that bt.Bitmap always returns null… I’ve worked around this for now by scrapping the entire TileCache object for the retrieval of tiles and just linked directly to the files… but I think it would be better if I could use BitmapTile.Bitmap in case the file naming convention changes for some reason. 
  
 .Ryan.

I have another question, and I think its more about LayerOverlay…  I’m seeing where if I go to a cached area, it loads really quick…  If I pan to an area that I don’t have cached, and then pan right back to an area I do have cached, the cached area doesn’t draw right away… it appears it is still waiting for the tiles of the old area to be downloaded, which can take awhile. 
  
 I noticed the DrawCore method was in a Background thread, although maybe all drawing is done on that one thread.  I was just curious if you had any ideas how I could get my cached tiles to draw right away? 
  
 Thanks, 
  
 .Ryan.

Ryan, 
  
 If the bitmap for column and row is null, I think there is no cached image on your disk, you can check the cache folder, 
 and find the overlay id sub folder, there should be some sub folders which represent row and column. 
  
 It’s hard to tell what was going on, you might provide a simple sample that you can debug on our side. 
  
 Thanks, 
 James

In addition to what Ryan asked (I would really appreciate to know why sometimes cache tiles are not shown right away as they should), Is there any way to abort/halt any current drawing/caching threads? 
  
     I mean, If certain zone is zoomed, the control start caching the view extent and a configured set of adjacent tiles. But if user moves, I guess the same process starts in the new zone while still tiles from the previous are being cached. Is there any way to tell the component "stop doing whatever you are doing" (including any queued refresh command or anything) so new extent can be drawn as fast as possible? 
  
  There are some times where the system gets frozen for some seconds when internal processes are being done, and this is what I would like to avoid.

Carlos,


  About caching, I let you know that we have drawing canceling working on the latest version of the Wpf Edition, so that it stops building cache as soon as the extent is changed by zooming in or out. But this is working only with shapefile layers. We don't have that implemented on background layers such as Google Map or Bing Map. I don't know what your scenario is exactly with your app but I let you know what to expect with the current version. For implementing drawing canceling for other layers such as Google Map or Bing Map, we have that in plan for developement in the future but I cannot give you a date. Thank you.



Hi Val, 
  
  That’s a surprise! From what version? My case is with ShapeFiles indeed. 
  Do you have any idea why the tiles does not come immediately even when they are pre-cached as Ryan pointed out?

Carlos,


 I would get the latest production build from the Customer Portal to make sure you have that improvment. I would not say that the drawing canceling thing has the effect of redrawing immediately when changing extent when the map has a lot to draw, but this is an improvement.