ThinkGeo.com    |     Documentation    |     Premium Support

Tiling Scheme customization

I'm using open street maps as my base map and I've got the source kit and so I have dug around inside the OpenStreetMapOverlay implementation and my question is about the tiling scheme that ThinkGeo uses. 


Currently a request for a 'tile' or region of the map comes into the overlay and it must stitch together 4 OpenStreetMap tiles, each having to be downloaded from the server before this single ThinkGeo tile can be rendered because the tiling scheme does not line up. 


I've helped allieviate this by adding a memory cache alongside the disk cache because typically an OpenStreetMap tile will be used several times in this stiching process and keeping it in memory for a bit helps.


However, we're deploying this to employees in the field, with air cards and relatively old hardware. Most of all I'd like to adjust the tiling scheme of ThinkGeo so that it requests a region of map that lines up with the OpenStreetMap tiling scheme so that each asychronous call to DrawCore has a GeoCanvas object that corresponds to a single tile on the server.


I obviously cannot alter the public OpenStreetMap tiling scheme. 



Hi Cody, 
  
 Could you please describe your question clearer? It seems that you thought we request 4 tiles from the server and stitch them together as one tile for displaying, in fact, we are doing the request in separate threads, each thread request a region of the map and display on the map. Do I misunderstand something? 
  
 Thanks, 
  
 Edgar

I was given a 'CustomOpenStreetMapOverlay' source file when we purchased the source kit and requested the OpenStreetMap overlay source. I'm certain it does this; the one I have anyway. This 'CustomOpenStreetMapOverlay' talks directly to the OpenStreetMap server and not a ThinkGeo WMS server proxy. 


What happens, like you said, is the Map spawns a thread for each region of the map. We'll call this a ThinkGeo tile. ThinkGeo divides the Map up into different tile sets than OpenStreetMap does. They do not line up and so you have to request multiple OpenStreetMap tiles in order to stitch together the entire region requested by the single ThinkGeo tile. 


Every ThinkGeo tile needs to consume multiple OpenStreetMap tiles in order to draw the entire ThinkGeo tile region. This means even for the same size ThinkGeo tile, you'll need from 2-4 OpenStreetMap tiles stitched together and croped to build the single ThinkGeo tile for the region referenced by the DrawCore call. 


This is how its working. If there is a newer implementation of CustomOpenStreetMapOverlay or CustomOpenStreetMapLayer I would love to look at it. I can post the code that I have here, but that is not something you normally provide to everyone as we paid for the Source Kit.


What I'd like is the ability to change a Property on the core Map object which sets how it divides the Map up into regions so that they would then correspond to OpenStreetMap tiles. This way each region of the Map that spawns a thread will be correspond to a single OpenStreetMap tile from disk, memory or the web.


My only other alternative is to change how OpenStreetMap divides their tiles, and I don't have control over that. This issue significantly affects the performance of our solution as the public OpenStreetMap server throttles requests and doing 4 requests for each tile quickly results in being refused service by the OpenStreetMap server.


-Cody



Hello Cody, 
  
 Thanks for your further information, looks like the CustomOpenStreetMapOverlay has some difference with the one I know, could you please send to forumsupport@thinkgeo.com
  
 We can make some compare and check the root cause. 
  
 Sorry for the inconvenience. 
  
 Regards, 
  
 Gary

Hi Cody, 



Thanks for bringing this to our attention! 



There are several players at work that make how OpenStreetMapOverlay gets its tiles a bit confusing. 



Let's start out with the lowest level of the OpenStreetMapOverlay, which is the OpenStreetMapLayer. The OpenStreetMapLayer is what performs the actual tile retrieval from the OpenStreetMap Server and provides the data to the OpenStreetMapOverlay. 



The OpenStreetMapOverlay also inherits from the TileOverlay and the TileOverlay uses a default Matrix. 



The OpenStreetMapOverlay uses the OpenStreetMapLayer to actually request tiles from OpenStreetMap and this OpenStreetMapLayer has a special set of math that defines how to properly request these tiles. The OpenStreetMapLayer is not specific about what tiles it returns, as we just pass it an extent and it returns us the tiles. So it cannot possibly know that the OpenStreetMapOverlay is using that default Matrix that we inherited from the TileOverlay. This disconnect between the OpenStreetLayer tile retrieval system and the Tile Matrix is what is causing the issues you are seeing.


So what we need to do is make the OpenStreetMapOverlay use a Tile Matrix that matches up with the tile request math found in the OpenStreetMapLayer. With these two elements matching up, the Overlay should then only request a single tile from OpenStreetMap for each tile within the Overlay's matrix. 

This is something that we are going to look into and will be able to implement into the WPF Desktop Edition binaries. 



Potentially you could override TileOverlay code to inject your own TileMatrix but this is not a very obvious option as the OpenStreetMapOverlay code is abstract and not something that would be normally included in the Source Kit.



Cody, 
  
   We are in the process of making the necessary changes and will update you soon.  One thing to make sure of is that if you use the OSM overlay than I suggest you pair it with the OSM zoom level set for the map and your other layers.  Having the scales for the map snapping with the overlay is critical if you want a 1:1 request ratio with OSM. 
  
 David

Cody,
 
The map by default is using DecimalDegrees’ zoomlevelset while OpenStreetMaps is using SphericalMercator zoomlevelset, the scaling system differences makes the tile extent not match, so needs stitching and cropping to get a required tile . Now from 6.0.180.0 we change the default zoomLevelSet to SphericalMercatorZoomLevelSet if Map.GeographyUnit equals to Meter, which helps the tile be required efficiently. (no stitching or cropping). 
 
We also fix an issue that OpenStreetMapZoomLevelSet was using Decimal Degrees’ ZoomLevelSet by mistake.  
 
If the map’s ZoomLevelSet is set to a different value than SphericalMercatorZoomLevelSet(the same as OpenStreetMapZoomLevelSet), it will still stitching and cropping to get the correct tile.
 
Please get the 6.0.180.0 and let us know if you have any issues. 
 
Thanks,
 
Edgar

Since I replaced my references with 6.0.329.0 the map throws a null reference exception upon drawing or refreshing. This happens with even 0 layers added to the map.

6.0.0.331 does the same thing.



Hi Cody, 
  
 Thanks for the further information and Sorry for the inconvenience, would you please show us the callstack of this exception or a highly appreciated small sample to reacreate this problem. 
  
 Waiting for your further information. 
  
 Johnny 


Object reference not set to an instance of an object. 
  
    at ThinkGeo.MapSuite.WpfDesktopEdition.WpfMap.DxQ=(IEnumerable`1 FBQ=, RectangleShape FRQ=, OverlayRefreshType FhQ=) 
    at ThinkGeo.MapSuite.WpfDesktopEdition.WpfMap.DxQ=(RectangleShape EBQ=, OverlayRefreshType ERQ=) 
    at ThinkGeo.MapSuite.WpfDesktopEdition.WpfMap.DrawCore(RectangleShape targetExtent, OverlayRefreshType overlayRefreshType) 
    at ThinkGeo.MapSuite.WpfDesktopEdition.WpfMap.Draw(RectangleShape targetExtent, OverlayRefreshType refreshType) 
    at ThinkGeo.MapSuite.WpfDesktopEdition.WpfMap./xM=(RectangleShape ABQ=) 
    at ThinkGeo.MapSuite.WpfDesktopEdition.WpfMap.Refresh() 
    at MapClick.ViewModels.MapViewModel.MapLoaded(Object sender, RoutedEventArgs e) in d:\Users\cvanzant\Projects\eCitation Trunk\MapClick\MapClick\MapClick\ViewModels\MapViewModel.cs:line 146 
    at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) 
    at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) 
    at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) 
    at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e) 
    at System.Windows.BroadcastEventHelper.BroadcastEvent(DependencyObject root, RoutedEvent routedEvent) 
    at System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(Object root) 
    at MS.Internal.LoadedOrUnloadedOperation.DoWork() 
    at System.Windows.Media.MediaContext.FireLoadedPendingCallbacks() 
    at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks() 
    at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget) 
    at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget) 
    at System.Windows.Media.MediaContext.Resize(ICompositionTarget resizedCompositionTarget) 
    at System.Windows.Interop.HwndTarget.OnResize() 
    at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam) 
    at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(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 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) 
  
  
 There is no inner Exception.

Hi Cody, 
  
 Please see the answer in your ticket. 
  
 Regards, 
 Edgar

Hey,  
  
 I managed to get around the exception by not clearing the Interactive Overlays, which I used to be able to do.  
  
 Anyway, I’m sorry to report the latest version 6.0.0.351, still requests 4 open street map tiles from the cache or server per DrawCore call.

Cody, 
  
 Please get the development build 6.0.351.0 to have a test, as I replied in my previous post, we set the Map.ZoomLevelSet to SphericalMercator when the unit is set to Meter, but this is an API change so we just did it in development branch (6.0.x.0). If you want to use the release branch (6.0.0.x), please add this code to your project, Map1.ZoomLevelSet = new GoogleMapsZoomLevelSet(); the request tile count is 1. 
  
 Regards, 
 Edgar

Thank you!

Welcome and any questions please feel free to let me know. 
  
 Regards, 
 Johnny