ThinkGeo.com    |     Documentation    |     Premium Support

Problems with TileCache

Hey,

It seems there’s few bugs in TileCache implementation. At least I think so.

First case is that if request to service fails, the non-existing tile is still cached. This prevents actual tile ever being fetched to local machine. See below, example of two tiles in my cache.

Other thing is that “blank” files are stored in local cache, each taking up 4Kb of HDD, that are way out of bounds of the layer. Say, I have few features in Finland but I’ll still end up caching a lot of “nothing” when panning/zooming over Canada.

Thanks.

Thanks Mikko,
What type of overlay or layer you are using. I tested with some overlay. It won’t cache the “bad” tiles. If you could provide use a demo project or code that will be very helpful for us to look into more details. We should never cache the “bad” tile.

Thanks

Frank

Sorry it took some time to respond.

var overlay = new LayerOverlay();
overlay.TileCache = new FileRasterTileCache(@"C:\some-path-here\TileCache", @"osm-wms.default.3857.raster");

var layer = new WmsRasterLayer(new Uri("https://ows.mundialis.de/osm/service?"))
{
	Credentials = null,
	DrawingExceptionMode = DrawingExceptionMode.DrawException,
	Name = "WMS"
};
layer.ActiveLayerNames.Add("OSM-WMS");
layer.ActiveStyleNames.Add("default");
layer.Crs = "EPSG:3857";
layer.OutputFormat = "image/png";

overlay.Layers.Add(layer);

Thanks Mikko,
Yes. We could re-produce this one. This because the error happen on the layer level. A LayerOverlay could have multiple layers. One layer has some problem we keep drawing for the overlay. So the overlay doesn’t stop drawing if one layer get error. it will keep cache the tile. We are looking into more detail see if we could find a better way to handle this case.

Thanks

Frank

Thanks Mikko,
I find a solution. The reason we cached the pink tile. The layer catch the tile error and draw it on the tile. So the upper level(Overlay) never know if there is any error in the layer level. So let do not catch the error in the layer level. the overlay level will get the error and draw error text without cache them. Here is the full code

 mapView.MapUnit = GeographyUnit.Meter;
        var overlay = new LayerOverlay();
        overlay.DrawingExceptionMode = DrawingExceptionMode.DrawException;
        overlay.TileCache = new FileRasterTileCache(@"C:\some-path-here\TileCache", @"osm-wms.default.3857.raster");

        var layer = new WmsRasterLayer(new Uri("https://ows.mundialis.de/osm/service?"))
        {
            Credentials = null,
            DrawingExceptionMode = DrawingExceptionMode.ThrowException,
            Name = "WMS"
        };
        layer.ActiveLayerNames.Add("OSM-WMS");
        layer.ActiveStyleNames.Add("default");
        layer.Crs = "EPSG:3857";
        layer.OutputFormat = "image/png";

        overlay.Layers.Add(layer);
        mapView.Overlays.Add(overlay);
        mapView.Refresh();

I did some test. It works very good at run time.

Thanks

Frank

1 Like

Hi Frank,

We’ve tried to use this mechanism and while it fixes the original problem with cached invalid tiles, it may have other problems.

Tested with WmtsLayer, and set the DrawingExceptionMode.ThrowException is enabled. But if there is a problem communicating to web server, I got this unhandled exception:

System.Net.WebException
HResult=0x80131509
Message=The remote server returned an error: (400) Bad Request.
Source=System
StackTrace:
at System.Net.HttpWebRequest.GetResponse()
at ThinkGeo.Core.WebBasedLayer.fx8=(WebRequest webRequest)
at ThinkGeo.Core.WmtsLayer.mTs=()
at ThinkGeo.Core.Layer.Open()
at ThinkGeo.Core.LayerOverlay.OpenCore()
at ThinkGeo.Core.MapViewBase.80o=(Overlay overlay, RectangleShape targetExtent, OverlayRefreshType refreshType)
at ThinkGeo.Core.MapViewBase.7Uo=(IEnumerable`1 overlays, RectangleShape targetExtent, OverlayRefreshType refreshType)
at ThinkGeo.Core.MapViewBase.DrawCore(RectangleShape targetExtent, OverlayRefreshType overlayRefreshType)
at ThinkGeo.Core.MapViewBase.50o=(Object sender, EventArgs e)
at System.Windows.Threading.DispatcherTimer.FireTick(Object unused)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at MapCoreTestClient.App.Main()

It’s difficult to put any try-catch statements in this call chain as the App.Main() is WPF auto-generated code. Is there any other way to handle this than changing the DrawingExceptionMode?

Thanks, Rasmus

Thanks Rasmus,
As I mentioned the overlay may not know what happen for the layers since the layer draw the exception and swallow the exception quietly. But we fixed this one for the WMSOverlay since the WMSOverlay only have one built-in layer. This will be much easier to handle the exception.
Here is the code
You can get the latest Beta release tomorrow and try with the code.

        mapView.MapUnit = GeographyUnit.Meter;
        WmsOverlay wmsOverlay = new WmsOverlay(new Uri("https://ows.mundialis.de/osm/service?"));
        wmsOverlay.SendingWebRequest += WmsOverlay_SendingWebRequest;
        wmsOverlay.TileCache = new FileRasterTileCache(@"C:\temp\TileCache", @"overlay-osm-wms.default.3857.raster");
        wmsOverlay.DrawingExceptionMode = DrawingExceptionMode.DrawException;
        wmsOverlay.Parameters.Add("layers", "OSM-WMS");
        wmsOverlay.Parameters.Add("STYLES", "default");
        wmsOverlay.Parameters.Add("SRS", "EPSG:3857");
        mapView.Overlays.Add(wmsOverlay);
        mapView.Refresh();

Thanks

Frank

Hey,

I might remember incorrectly but I think there was some problems with WmsOverlay in the past so we opted to put WmsRasterLayer on LayerOverlay. Anyway, we’ll try the WmsOverlay in this case.

Does this fix apply to WmtsTiledOverlay, too?

Thanks Mikko,
Yes. I tested the WmtsTiedOverlay too. It won’t cache the bad tile. Here is the test code.

 mapView.MapUnit = GeographyUnit.Meter;

        var wmtsOverlay = new WmtsTiledOverlay(new Uri[] { new Uri("http://www.openbasiskaart.nl/mapcache/wmts") }, WmtsSeverEncodingType.Kvp);
        wmtsOverlay.DrawingExceptionMode = DrawingExceptionMode.DrawException;
        wmtsOverlay.ActiveLayerName = "osm-epsg3857";
        wmtsOverlay.ActiveStyleName = "default";
        wmtsOverlay.TileMatrixSetName = "epsg3857";
        wmtsOverlay.OutputFormat = "image/png";
        wmtsOverlay.TileCache = new FileRasterTileCache("C:\\temp\\wmtsOverlay");
        mapView.Overlays.Add(wmtsOverlay);
        mapView.Refresh();

Thanks

Frank

1 Like

Thanks Frank4,

I changed our WmsLayer+LayerOverlay implementation to use WmsOverlay instead and this seems to work alright. WmtsOverlay works, too, but I still “feel” like this is performing poorly compared to using WmtsLayer + LayerOverlay (which is not lightning fast either, at the moment). I got the latest binaries on friday.

Thanks.

Thanks Mikko,
WmtsLayer + LayerOverlay should the same as the WmtsOverlay. WmtsOverlay just a wrapper for the WmtsLayer. WmtsOverlay only have one WmtsLayer. This will make us easier to control the exceptions.

Thanks

Frank

I thought that was the case and I really didn’t do any accurate comparison. I just used our app as usual with both layer setups for a while, with same endpoint, and that was the “feeling” I was left with. But thanks.

Thanks Mikko,
I am going to close this one. Go ahead let us know if you have more question.

Thanks

Frank

Hey,

It definitely is way slower using WmtsOverlay. Maybe I provide a side-to-side comparison example.

Thanks,
Yes. A side to side comparison example will be very helpful for us to look into more detail.

Thanks

Frank

In a simple app differences aren’t apparent. Maybe it’s something in my other code (so I won’t provide example after all :slight_smile: )

Thanks Mikko,
We will review our code too. Go ahead let us know if you have more question.

Thanks

Frank