FillEmptyTiles fork

Topics: General
Feb 15, 2011 at 9:49 AM

Hi Radioman,

I have checked in code in the FillEmptyTiles fork that

  • Extends the existing smooth zoom logic to tile retrevial from cache or web server so that most blue error tiles (HTTP 404 or tile not in cache) are no longer generated. Instead GMap.NET will work more like the mapping web sites and fill in a missing tile with a resized lower level tile.  This particularly helps generate a better looking display when running in Cache Only mode when not all tiles have been downloaded or when looking at an area where the level of available data changes.
  • Sets the maximum zoom level for Google, Bing and NearMap to similar level to what can be seen on their web sites.

Some comments about the details of the change:

I set MaxZoom to 19 for Bing, 21 for Google and 22 for NearMap. If you had a reason for not going to this level of zoom that I do not know about please feel free to change it.

NearMap allows zooming on their web site to level 24 (but I did not see any data past level 22) but I found GMap.NET failed at zoom level 23 with a numeric overflow of an int value in calculating tile pixel locations.

I added a LRU cache of requests that returned an HTTP 404 Not Found error to prevent requesting that tile again.

To avoid needing to add locking I changed the memory cache data format (as held in KiberTileCache etc) from MemoryStream to Byte[]. This needed code changes in quite a few places. Howerver I thought it was better to have the tile cache holding stateless data.

The Windows Forms GMapControl now has FillEmptyTiles and MarkFilledEmptyTiles properties settable from designer.  The WPF GMapControl just has FillEmptyTiles. If FillEmptyTiles is set false it will switch off the change in the tile retreval code as well as the drawing code.

The smooth zoom code had been commented out in the WPF code but I enabled it (with required modifications). If there had been some reason for disabling it that I am not aware of please feel free to comment it out again.

Be careful as the way I made the change to allow for missing tiles was to add resizing data to the tile (rather than do image resizing in the tile retreval code) that the tile drawing code needs to handle. It might create compatability issues if there is other tile drawing code - like Windows Mobile that I did not look at.





Feb 15, 2011 at 11:53 AM

thanks, i'll go to review it

Feb 16, 2011 at 11:34 AM

i see, you changed MemoryStream to byte[], how do you dispose it ;/

Feb 16, 2011 at 4:36 PM

i've just tested it, seems to work quite good, but ;} it has some issues on different projections, wrong tiles are loaded... which i intend to fix

Feb 17, 2011 at 12:44 AM

Hi Radioman,

The change from MemoryStream to byte[] was needed becase I found multiple threads trying to decode the cached image from the same MemoryStream were failing becase they were both reading at the same time and that changed the current position for the other thread. One option was to add a lock on the MemoryStream but I thought it better to change to a byte[] that would not need a lock becase the data does not change. I thought it was Ok to leave the byte[] to be garbage collected normally without doing more.

Can you give more clues about what diffect projections were failing? You clearly have a lot more experience in testing all the variations that I do.  


Feb 17, 2011 at 8:57 AM

hm, only one thread should decode one tile, it's queue based processing, never locked for me, can you point the exact location?

Feb 18, 2011 at 12:01 AM

In Core.cs the ProcessLoadTask() function is run from 5 threads. It calls GMaps.Instance.GetImageFrom(...) to get an image from memory/cacheDB/web. Before my change the queue of requests that drives ProcessLoadTask() would always have resulted in always resulted in a particular image only being decoded by one thread at a time. However I changed ProcessLoadTask() to make it handle the case of an image not found by searching lower zoom levels. In the case where you zoom in 1 level past the available data there can be 4 threads that all look for separate tiles, don't find them (becase there is no data that this zoom level) and then all threads try to get the same image from the lower zoom level. (They will pass back data indicating that they want to display different parts of the image when it is drawn to the screen.) If the treads find the lower zoom level image in KiberTileCache and need to decode it, they can be in the image decode function (now GMaps.Instance.ImageProxy.FromByteArray(...) in my code) at the same time for the same image. I hope this makes it clear.

This is here I considered locking the MemoryStream but decided that changing lots of code to avoid added a lock was better option.

Feb 18, 2011 at 9:11 AM


Feb 26, 2011 at 9:52 AM

Hi Radioman,

I am just looking at the "wrong tiles loaded" issue you noted. I have not had time before now. Can you give an example of how to create the problem you saw? I have seen something that might be the same bug or might be different. It appeared when I zoomed in and out fast, but everything worked Ok if I zoom slowly. I have also spotted another bug with an easy fix related to PergoTurkeyMap. I would like to get everything working before I update the fork.




Feb 26, 2011 at 4:54 PM

i see, well it's related to map-types with non-standard tile offsets like i guess each projection should have some function to return lower zoom tile

Mar 11, 2011 at 5:24 AM

Hi Radioman,

I have updated the FillEmptyTiles fork with code to handle projections.  Would you like to try it again?

While I was testing I noticed that one of the maps (sorry I do not remember whick one) was generating GIF images that the WPF code did not handle, so I added that too.


Mar 11, 2011 at 2:28 PM

looks great! Now i need somehow put everything in the main branch, thanks a lot

Mar 11, 2011 at 11:08 PM

after some testing, i've found why in FindCoveringImages i get numTilesX = 2; or numTilesY = 2; unfilledLatLng is with low precision, need some other way to get correct pixels using MapsLTProjection.

i'll do some experiments...

Mar 13, 2011 at 10:12 AM

numTilesX and numTilesY are supposed to be set to 2 sometimes when projections like MapsLT result in tiles at different zoom levels not lining up. I was thinking that the worst case that needs to be handled is something like this: the point there the corners of four tiles meet in the upper zoom level lies in the middle of a missing tile.  To handle this I am looping through all the tiles in the upper zoom level that can partly overlay on the area that remains of the missing tile. This can result in needing to process 1, 2 or 4 tiles at the current zoom level.

Was there something actually going wrong? An exception or bad output? If so please describe it so I can fix it.

Mar 13, 2011 at 3:43 PM

i see, just figuring out how it really works..