MapDotNet UX Help

Tile caching is useful in many scenarios, particularly for static data that does not change frequently. Determining the best caching approach is not trivial as there are many interrelated factors to consider:

  1. How large is the extent of the cache - what geographic region is to be cached?
  2. How many zoom levels and to what maximum zoom level do I need to cache?
  3. How many combinations of layers do I need to cache?
  4. How often does the cache need to be refreshed?
  5. How quickly can a cache be created with a given set of hardware?

Based on the answer to these questions you may find yourself up against an insurmountable requirement. For example, if your challenge is to cache the entire continental US from zoom level one all the way down to zoom level 20 and your map has five layers all of which must be randomly accessible to the end-user and cached, then you will have to rethink your requirement. The number of tiles to cache in this scenario is roughly 55 billion tiles. If your rendering farm is able to render say 150 tiles per second, then it would take roughly 11.6 years to render your cache; probably several years longer than your application's lifespan.

To compute an estimated number of tiles in a cache, use the following formula:
Ntfe * (4 ^ (Zmax - Zmin)) * 1.3 * Lp
    Ntfe- number of tiles at the full extent of your data at the minimum zoom level
    Zmax - max pyramid zoom level
    Zmin - min pyramid zoom level
    Lp - layer permutations (for example if you have 5 layers with complete random visibility access, this results in 31 permutations or (2^L)-1

How can I reduce the number of tiles in my cache?

  1. Reduce the number of zoom levels. A rule of thumb is, if you cache several zoom levels above a given level, you add about 33% more data to your cache.
  2. Do not cache to a deep zoom level. This is far more significant than the number of zoom levels. Every time you add another zoom level down, you increase the number of cached tiles by four times your current level and three times the sum of all of the tiles above [assuming several levels deep.] This non-linear increase causes your total cached tile count to rapidly get out of hand.
    • Consider not caching at the deepest zoom levels and let MapDotNet UX render map tiles as needed on the fly.
  3. Consider using a geometry layer which renders locally in your RIM application at lower zoom levels. This may have the following advantages:
    Deeper zoom levels may appear faster than downloading bitmap tiles at the same level
    Once you have the geometry for a tile at a given zoom level, then you have the data for all subsequent requests for deeper levels (no need to go back to the server)
    Geometry layers lower your server processing load by shifting it to the client
  4. Reduce your extents - this is obvious and of course limited by the requirements of your application.
  5. Make use of polygon caching regions. For example, if you need to cache data for the state of Florida, you should not set a rectangular region and cache empty tiles over the Gulf of Mexico. Instead, use a polygon to follow the state boundary in your cache preload configuration.
  6. Reduce the number of layer group permutations. Again, this may be limited by your application.
    • Instead of complete random-access for a set of layers where visibility is individually selectable, use layer groups. For example, for city utilities you might have water related and electrical in separate groups resulting in only two permutations instead of (2 ^ number of layers) - 1.
    • Increase the number of tile layers in your RIM application. If you have three data layers with random access assigned to one bitmap tile layer in your RIM application, then the resultant permutations which must be cached is seven. If instead you create three separate tile layers in your RIM application, then there are only three permutations (one data layer for each tile layer) to cache. The downside is multiple tile layers in your client application may take longer to load and this approach becomes unusable once you get over a fairly low number (e.g. 3 to 5 layers)

How do I create a Tile Cache in Studio?

  1. Right-click on a map running on a MapDotNet UX Server and select Create Tile Cache...(note this feature is not available for maps built on the local Studio Engine)
  2. Click the Begin Drawing the Region button to start drawing a polygon cache region. Click on the map to add your first node and then click again on the map to add subsequent nodes. Nodes may be added and removed as your draw by right-clicking a node. You may also left-drag nodes to reposition them.
  3. After drawing a polygon region, click End Drawing the Region to finalize your polygon cache region.
  4. Optionally select Overwrite Existing Tiles is you which to force the writing of a tile to cache even if the content is already in the cache. You might leave this unchecked if you are running a preload task again and do not need to discard previously cached content. If you change symbology of your map and wish to replace cached content then check this flag.
  5. Choose an optional Cache Name - this is used on the server to direct cached data to the appropriate caching database and table (see below) - if left blank the default cache is used.
    Animate and Highlight options are for visualizing a preload cache job running right in Studio - this is fine for small caching jobs but you will want to export your settings and use the standalone preloader for larger jobs.
  6. Export Cache Settings will write a cache preloader configuration file to a specified location on your file system for use by the standalone preloader.
  7. The Layer Groups section allows you to add the layer permutations you need to cache to the job. If left blank, a default group with all layers visible is used.

Notes on preloading using Studio

  1. The bleed ratio set in Studio (see Tools : Options : Bleed Ratio) must match the bleed ratio used in your application (see your MapRequest.BleedRatio on the descriptor for your layer using an MDNSMapTileRequestor). If they do not match, cached tiles will not be used by your application. Tiles with different bleeds are always considered different tiles.
  2. Studio preloading and the computed tile count does not become enabled until you are done drawing your preload region.
    How do I use the Standalone Cache Preloader?
    To use the standalone preloader simply copy your Cache Settings file created in Studio to where you intend to run the preloader. Then launch the preloader with the following options:

MapDotNet UX Tile Cache Preloader Version 1.0.0 - Copyright 2008, ISC

TileCachePreloader [path] -c -b? -i? -rt? -t? -p?
[path] required path to tile cache preloader config file
-c optional close console automatically on completion
-b? optional bleed ratio from 1.0 (default) to 2.0
-i? optional image type (jpg, gif, png[default])
-rt? optional request threads (defaults to 4)
-t? optional timeout seconds (defaults to 30)
-p? optional projection EPSG (defaults to 3785)

Notes on the preloader

  1. The default bleed in the preloader is 1.0 or (no bleed) - The bleed ratio set on the preloader (see -b option) must match the bleed ratio used in your application (see your MapRequest.BleedRatio on the descriptor for your layer using an MDNSMapTileRequestor). If they do not match, cached tiles will not be used by your application. Tiles with different bleeds are always considered different tiles.
  2. The -p option is for future use - right now only EPSG:3785 popular Spherical Mercator is supported.
  3. If you are skipping tiles due to timeouts - increase the timeout -t value
  4. It is critical that the MapRequest instance in your application matches the one constructed by the preloader. This means, image type, bleed ratio, layer visibility state, etc. must match. The MapRequest is hashed and this hash value becomes the cache-key in the tile repository.

Creating Large Tile Caches

  • Although SQL Express works well for small caches, it will degrade in performance as the cache size approaches the maximum database size allowed for Express
  • Make sure the source data you are rendering from is properly spatially indexed. For example, proper usage of the LOW, MEDIUM, HIGH settings can easily double the performance of SQL2008 indexes
  • The UX Tile Cache Preloader preloads from lower zoom levels (higher map scale) to higher zoom levels (lower map scale.) If your data becomes less dense as you drill down during preloading, then you should expect rendering speeds to increase and TPS (tiles per second rendering) to go up. If this happens, then you are bound by the renderer and your source spatial data. If on the other hand, TPS goes down, then you are bound by your tile cache database as it inserts new records.
  • For very large datasets, the IIS worker processes can consume large amounts of memory based on the following rule of thumb:
    • Memory in bytes = average features per tile * the number of concurrent rendering threads [-rt setting on preloader]  *  average vertices per feature * 256
    • If you use up all memory on your rendering machine, the .NET framework cannot allocate from the heap and you will get a System.OutOfMemory exception. Try reducing the number of concurrent preloader requests (lower -rt). Also, reduce the size of your web garden if you are using one.
    • For very dense datasets you can also limit the number of features rendered per tile by setting in the UX Services web.config ()
  • For maximum tile cache loading speeds (highest TPS), your spatial database and tile cache repository should be on separate machines. The UX rendering service (map service) can run on either but network I/O between the two should be high-speed, preferably a 1GBps switch.  

How to setup Map Caching on the Server

1. Create table in a database for tile storage

Scripts for PostGIS

id serial,
cachekey varchar(28) NOT NULL,
mapid varchar(256) NOT NULL,
item text NOT NULL,
writetime timestamp NOT NULL,

-- ALTER TABLE "mapCache" OWNER TO pguser;

-- DROP INDEX "mapCache_cachekey_uidx";

CREATE UNIQUE INDEX "mapCache_cachekey_uidx"
ON "mapCache"
USING btree

Scripts for Microsoft SQL Server 2008

CREATE TABLE [mapCache](
[id] [int] IDENTITY(1,1) NOT NULL,
[cachekey] [varchar](28) NOT NULL,
[mapid] [varchar](256) NOT NULL,
[item] [text] NOT NULL,
[writetime] [datetime] NOT NULL,

-- DROP INDEX [mapCache_cacheKey_uidx];

CREATE UNIQUE NONCLUSTERED INDEX [mapCache_cacheKey_uidx] ON [mapCache] ([cachekey]);

2. Update the   in the MapDotNet UX Services web.config file located by default at 'c:\program files\mapdotnet\7.0\service'

Uncomment the MapCacheAssemblyName, MapCacheClassName and the appropriate database connection lines.

Sample entry in Web.config




3. Refresh the MapDotNet UX Server in IIS