ThinkGeo.com    |     Documentation    |     Premium Support

Online maps and tile cache

Hi,

  1. Like said before we use overlay.DrawingException now to draw custom exceptions but I guess that same (throwing any other that OperationCanceledException) applies to that also ?

  2. I also noticed that after exceptions are drawn and you set layers IsVisible to false and refresh the map empty tiles are stored to cache for those tiles which had exception drawn. I discovered this on our app with OgcApiFeatureLayer but can be reproduce also with that sample you gave earlier HandleException_04112025.zip. I just added button to UI with this kind of handler:

     private void ChangeVisibility(object sender, RoutedEventArgs e)
     {
         _wms.IsVisible = false;
         _ = MapView.RefreshAsync();
     }
    

Br, Simo

Hi Simo,

  1. Yep, that’s right.

  2. I see. It’s because _wms.IsVisible = false prevents _wms from being rendered, thus DrawingException will not be raised and our custom Exception will not be thrown. As a result, Overlay would think the tile is valid so just go ahead and cache it.

You can just set the overlay instead of the layer to InVisible, as following:

   private void ChangeVisibility(object sender, RoutedEventArgs e)
   {
       //_wms.IsVisible = false;
       staticOverlay.IsVisible = false;
       _ = MapView.RefreshAsync();
   }

Thanks,
Ben

Hi,

Will that 2. work differently on 14.3 ? We can have multiple OgcApiFeatureLayers with one overlayer and we would like to control visibility of each layer individually.

Br,Simo

Hi Simo,

  1. No more workaround is needed in v14.3. You don’t need to throw in the DrawingException handler anymore. Simply doing wms.IsVisible = false will not cache any illegal tiles—so you’re free to use it. We’ve updated the HandleException HowDoI sample for v14.3. Take a look here:
    https://gitlab.com/thinkgeo/public/thinkgeo-desktop-maps/-/blob/develop/samples/wpf/HowDoISample/Samples/Miscellaneous/HandleExceptions.xaml.cs

  2. Instead of putting multiple OgcApiFeatureLayer s into a single LayerOverlay , another option is using separate OgcApiFeaturesOverlay instances. Each overlay supports “progressive rendering”—for example, you can draw the first 500 of 1,000 features immediately, then load the rest in batches. It doesn’t mean this is always better than LayerOverlay, here are some quick pros and cons:

  • Memory footprint:
    One LayerOverlay with many layers uses less memory than several overlays.
    LayerOverlay with multiTile mode can draw the data by tiles.
  • Redraw behavior:
    Toggling visibility on a layer inside a LayerOverlay forces all its sibling layers to redraw. Hiding or showing an entire Overlay only affects that overlay.
  • Progressive loading:
    The WFS 2.0 / OGC API – Features spec supports progressive feature delivery, and OgcApiFeaturesOverlay can take full advantage of it by setting DrawingBulkCount to control batch size.
  • Here’s a sample that shows exactly how to set it up:
    samples/wpf/HowDoISample/Samples/MapOnlineData/OGCAPIFeatureServer.xaml.cs · develop

Thanks,
Ben

Hi,

Thanks, I think that we have now solved all issues related to cache.

I have still one more question. We would like to have possibility to download maps to cache before going to area without or with limited network connection. It means that user could select area and click a button and tiles would be downloaded to cache automatically (for next n. zoom levels)

Is there any support on ThinkGeo for this kind of functionality which could be used or what would be the best way to implement it ? It would be also nice if this could be done on the background without using the map visible to user.

Br,Simo

Hi Simo,

We do have all the pieces, but I don’t think there’s a straightforward way to accomplish this without writing some code / understanding the Matrix system. Let us think more about it and see if we can add some simple APIs to accomplish it in v14.3.

Thanks,
Ben

Hi,

Thanks, it would be great if you could have some builtin support for downloading tiles to cache for different kinds of online maps. Let me know if you came up with some solution.

Br,Simo

Hi Simo,

We’ve added a couple of new samples to HowDoI :

The first sample demonstrates how to pre-generate caches for a: TileOverlay :

It shows how to:

  1. Pre-generate cache tiles within a given extent and zoom range using a single line of code.
  2. Apply a different tile matrix set to a TileOverlay and generate the cache based on that matrix.

We also added a sample for generating tiles for an XyzLayer . It’s similar to the one above but specifically targets XYZ-based layers:

Thanks,
Ben

Hi,

Thanks, it looks good and this API is available on coming 14.3 release ?

Br, Simo

Yep. We are doing the final test / API reviews now and the release will be available next week.

Hi,

We finally had time to test these new API’s for generating tile cache.

One issue was noticed when generating tile cache is that if for example we select an area like 3kmx3km and in first needed zoom level it consist 36 tiles (in this case 256x256 tiles).

Then we download tiles from that zoom level and from next 3 levels (4 levels in total). Since tile count is duplicated roughly by four when going to next zoom level this would cause a quite high number of download requests to server (>2200 in total). Some of the servers think this kind of request flow in short period as a DDos attack and suspend service.

What are your thoughts about this ? Could you do the downloading e.g. in chunks with some delay if there is lot’s of tiles to be downloaded instead of downloading them all in parallel ? Or some other ways to prevent this kind of problem to happen ?

Also, it was noticed that when lot’s of tiles are downloaded some of them might fail due different (network/server related) reasons. Of course those can be downloaded by triggering download again to same area but maybe somekind of re-attempt could be added to your side as an improvement ?

Br, Simo

Hi Simo,

LayerBase.GenerateTileCacheAsync does not send all tile requests at once. It currently allows up to 4 tile jobs to run concurrently, and each job starts the next tile immediately after finishing the previous one.

A quick way to slow it down is to pass in a callback action to the method:

Layer.GenerateTileCacheAsync(... Action<TileCacheGeneratedLayerBaseEventArgs> progress, ...)

And do Thread.Sleep(100) in this Action.

A better way is to customize the passed-in layers and add the delay logic in the layer itself: For sync Layer, override DrawCore() and do Thread.Sleep(100); For AsyncLayer, override DrawAsyncCore() and do await Task.Delay(100). For web-based layers such as WebXyzRasterTileAsyncLayer, a more precise option is to override methods such as GetTileAsyncCore() or FetchImageAsyncCore() so the throttling happens closer to the actual HTTP request.

Regarding failed tile downloads, there is no built-in retry mechanism in this API at the moment. If you need retry, the best option is to add it in a custom layer override, again in methods like
GetTileAsyncCore() or FetchImageAsyncCore(). Also, set LayerBase.DrawingExceptionMode = DrawAndThrowException or ThrowException to make sure exceptions are not silently ignored.

Let me know if you see any more issues.

Thanks,
Ben

Hi,

I saw that limit of 4 concurrent requests from the implementation but still lot’s of requests are sent in short time period. It would be nice if this value could be set via API instead of hardcoded value. Atleast based on our experiments some servers work much better when only one request is sent at the time.

Yes, I know that we can do those things by overriding your implementation but actually I meant that maybe you could improve the API and functionality so that it would be possible to pass in parameters like chunkSize/delay which could be used to control the throttling without the need to implement it by everyone who might need it. I would think that this kind of throttling would be needed quite a lot to prevent server thinking cache download is a DDos attack.

Same applies also for the retry mechanism, maybe it could be also built-in with parameter in API for count ? It would probably benefit also other users.

Br, Simo

Make sense! I’m thinking creating an option class like following and pass this into that method. I’ll let you know once it’s settled down.

  public class TileCacheGenerationOptions
  {
      public int MaxConcurrency { get; set; } = 4;
      public TimeSpan TileDelay { get; set; } = TimeSpan.Zero;
      public int RetryCount { get; set; } = 0;
  }

Hi Simo,

We’ve added the following Options class to the latest v15.0.0-beta007:

 public class TileCacheGenerationOptions
    {
        public int MaxConcurrency { get; set; } = 4;
        public TimeSpan TileDelay { get; set; } = TimeSpan.Zero;
        public int RetryCount { get; set; } = 0;
        public TimeSpan RetryDelay { get; set; } = TimeSpan.Zero;
    }

which is accepted in both static LayerBase.GenerateTileCacheAsync() and RasterXyzTileAsyncLayer.GenerateTileCacheAsync(). The latter is easier/faster if you only download raster tiles from one server.

Thanks,
Ben

Hi,

Thanks, once again for your fast support.

Br, Simo

You are always welcome!