Tips & Tricks: GeoWebCache Tweaks Checklist

Dear All,
during a discussion I recently had  with a client about how to put together an enterprise deployment of GeoWebCache we went over a few tweaks coming from previous deployments which I thought would have been interesting to summarise on a blog post

Client side caching of tiles

GeoWebCache, as most tile caching systems, is extremely fast and efficient at serving out tiles therefore you might not think about fiddling with client-side caching. Nevertheless, in our experience we found out it is usually good practice to always try and explicitly control the HTTP Caching Headers for tiles served via GWC, especially when the layers for which the tiles are generated rarely change (or do not change at all) and/or when they are background layers for your maps, which means clients will be requesting them over over with high frequency.
By enabling HTTP client-side caching we ask the users’ browsers to cache, on first access, the tiles generated by GWC in their local temporary cache of internet pages achieveing that no GWC requests for a certain tile will be made more than once as long as the tile caching header will tell the browser “Hey, I am still valid”, which could be potentially forever.Loking at GWC configuration examples, we can control HTTP Client Side caching via the following configuration elements of the <wmsLayer> element:

<!– OPTIONAL The number of seconds that a client should cache a tile it has received from GWC. The default is to use the same expiration time as the WMS server provided. If this value is not available, 2 hours is used. –>
<expireClientsList>
<expirationRule minZoom=”0″ expiration=”7200″ />
<expirationRule minZoom=”10″ expiration=”600″ />
</expireClientsList>

With this options we can decide to set the client side expiration of tiles selectively for zoom level to zoom level to a certain number of seconds, allowing us to set different expiration times for differen zoom levels if needed . As an instance if we wanted tiles for a layer at al zoom levels to expire after 1 week we could use the following fragment:

<expireClientsList>
<expirationRule minZoom=”0″ expiration=”604800″ />
</expireClientsList>

There is a few things we need to know about this approach.
First of all this won’t work if the users use the browser in private mode (this is common in some restricted environments, like military, police and so on) or better it will work as long as the browser don’t get closed since at the point that local cache will be erased. Furthermore, once the tiles gets cached on the users browsers they will not see any update in the tiles from GWC until the tiles actually expire (as we intructed the browsers to do), therefore if by mistake we would need to update tiles for whatever reason while they haven’t expired yet, users won’t see the changes unless they clean up their local caches.

Now, ho do we check if this mechanism is working? It is easy, let’s check with Chrome’s Javascript Console what the HTTP Response Headers for a given Tile are and look for the proper ones.

As shown above the interesting headers are as follows:

Cache-Control:max-age=604800, must-revalidate
Date:Sat, 26 May 2012 09:43:51 GMT
Expires:Sat, 02 Jun 2012 09:43:51 GMT

which tell us that this tile will be valid for 2 weeks since its first acquisition. By the way to make also sure we are getting this tile from cache on the subsequent acceses you might want to check the Network tab and search for the (from cache) sentence on the Size column.

Using format modifiers

It is extremely important to get tiles from the back-end WMS as fast as possible (e.g. to reduce seeding time), as small as possible (to reduce the needs of disk space) but also with the highest possible quality; there is clearly a trade off between the first two requirements and the last one.

Trading off between these requirements usually mean tweaking the following parameters:

  • response format for the back-end (png VS png8 VS jpeg)
  • compression
  • usage of precomputed palettes
which can be controlled via the <formatModifiers> element, which canbe used locally on a per wmsLayer basis or globally, for all layers. Here below you can find an example
<!– OPTIONAL Format modifiers. These can also be defined per layer –>
<formatModifiers>
 <!– You can have one or more of these elements –>
 <formatModifier>
   <!– REQUIRED This format modifier applies to JPEGs –>
   <responseFormat>image/jpeg</responseFormat>
   <!– OPTIONAL PNG images are requested from the backend to avoid double compression –>
   <requestFormat>image/png</requestFormat>
   <!– OPTIONAL For request formats that support it  –>
   <transparent>false</transparent>
   <!– OPTIONAL Background color –>
   <bgColor>0x0066DD</bgColor>
   <!– OPTIONAL Palette used on WMS server –>
   <palette>somepalette</palette>
   <!– OPTIONAL Tune the compression level, 1.0 is best quality –>
   <compressionQuality>0.9</compressionQuality>
 </formatModifier>
</formatModifiers>
Let’s now cover some real world uses cases, starting from ortos.
Usually we want to serve ortos as background layers for our maps, hence we do not need transparency for them as we won’t be overlaying them on top of something else; if this is true I would recommend instructing GWC to save them as JPEG using a compression level of around 0.75 (visually lossless), to do this you need to instruct GWC to request the tiles as PNG (or PNG8) and encode them locally as JPEG to avoid double compression.
Since we are there I would also recommend to try and see if we can put a precomputed palette in the mix to greatly reduce the size of the images you are getting from the WMS as well as rendering time (especially if the WMS is a GeoServer); here you can can find additional information about how create and set up a precomputed palette for a WMS layer in GeoServer. Beware that depending on the dynamic of the colors in the layer a palette might not always be suitable or anyway might sometimes produce bad quality imagery.
Here below you can see how we could achieve something like that for an ortofoto layer in GeoServer by teaking the GWC configuration:
<formatModifiers>
 <formatModifier>
   <responseFormat>image/jpeg</responseFormat>
   <!– PNG images are requested from the backend to avoid double compression –>
   <requestFormat>image/png8</requestFormat>
   <transparent>false</transparent>
   <bgColor>0xFFFFFF</bgColor>
   <!– Palette used on WMS server –>
   <palette>orto</palette>
   <!– Tune the compression level, –>
   <compressionQuality>0.75</compressionQuality>
 </formatModifier>
</formatModifiers>
This configuration should give us good quality but also, high speed when it comes at seeding as well as low memory. Of course your mileage may vary depending on how good the palette is for the layer at hand.
Other interesting use case, complex vector layers with streets, buildings, etc. etc.
In this case we usually tend to retain PNG as the serving format to keep transparency and/or translucency where needed, however, especially since when we have the new translucency aware PNG8 quantizer in GeoServer trunk I would resort to ask for PNG8 from the back end GeoServer since 99% of the time 256 colors are enough to create beatiful renderings but would also allow to cut size of the output images as well as rendering times (mind you, we are capable of inverting translucent palettes, that is palettes where individual colors neither opaque nor transparent, but this process is slow and compares to creating a new palette on each request, hence I would suggest to test the two aproaches and select the best one depending on your data).
In this case I would advise to try to:
  • set up a proper palette in GeoServer if that is possible given the specific layers
  • use a format modifier that ask for PNG8 (possibly with a precomputed palette) to GeoServer then store and server as PNG (See format modifier below)
<formatModifiers>
 <formatModifier>
   <responseFormat>image/png</responseFormat>
   <!– PNG images are requested from the backend to avoid double compression –>
   <requestFormat>image/png8</requestFormat>
   <transparent>true</transparent>
   <bgColor>0xFFFFFF</bgColor>
   <!– Palette used on WMS server –>
   <palette>roads</palette>
   <!– Tune the compression level, –>
   <compressionQuality>0.75</compressionQuality>
 </formatModifier>
</formatModifiers>

Generally speaking we can use translucent PNG8 with or without precomputed palette when disk size is an issue while speed of generation is not and let GeoServer create an optimized palette on each request if the precomputed approach does not yeld any speed increase. At that point I would also try to use metatiling in GWC to request larger tiles from the back-end and mititgate the potential reduction in speed.

Controlling Metatiling

This is mostly specific to people using GeoServer as a back-end, but yeah, you are already using GeoServer no? If not, what are you waiting for? 😛

Joking aside, GeoWebCache can do Metatiling itself which means that when it has to generate a tile it can be instructed to request from the back-end WMS a much larger image (the so called metatile) and then chunk it in smaller tiles in memory before writing to disk.
This is important to, on a side, reduce the number of concurrent requests on the WMS back-end but, on the other side, to largely reduce (but, beware, not avoid) duplicate labels for geometries (which is a side effect of tiled request over vector layer containing large geometries with labels).

Looking at the GWC configuration this can be controled via the <metaWidthHeight> element of the <wmsLayer> element as shown below:

<!– OPTIONAL The metatiling factors used for this layer  If not specified, 3×3 metatiling is used for image formats –>
<metaWidthHeight>
<int>4</int>
<int>4</int>
</metaWidthHeight>

With the configuration above GWC will turn a 256×256 tile request into a 1024×1024 WMS request for the back-end WMS. It is worth to point out that:

  1. Most public WMS server do have limitations in place with respect to the maximum size in pixels of a WMS request, hence make sure to not exceed them
  2. There is an implicit trade-off between the size of the image we generate and the load on back-end WMS. Although larger images reduce the contention on the WMS server (less files open, less DB connections and so on) asking for a very large image may need a lot of memory to process on the server. Usually 3×3 or 4×4 is a good factor
It is relevant to introduce also other parameters which related to metatiling.
First of all we can instruct the back-end GeoServer to NOT do metatiling itself via the following element of the <wmsLayer> element:

<!– OPTIONAL The TILED= value to be sent to the backend server.  Should normally be omitted –>
<tiled>false</tiled>

This is relevant in those, infrequent cases when you don’t want to have metatiling done at all at any level. This parameter tells GWC to appen the GeoServer vendorOption TILED=yes/no to the WMS request to instruct it to NOT try and do any metatiling.

It is also important in some cases to introduce the <gutter> element as shown below:<!– OPTIONAL The gutter is specified in pixels and represents extra padding  around the image that is sliced away when the tiles are created. Certain WMS server have edge effects that can be elimited this way, but it can also result in labels being cut off –>
<gutter>0</gutter>

It can be used to request tiles slightly larger (in the pixel domain) than needed from the back-end WMS to remove image processing border at the effect, or in some cases, remove attributions (which we do not advise you to do!)

Proxying to multiple WMS Servers

Not many people know that GWC can proxy requests for a layer to multiple WMS servers directly, by simply round robin individual requests to them.
This can be achievede by the <wmsUrl> element of the <wmsLayer> element, by appending a list of valide WMS URL as follows:
<wmsUrl>
<string>http://serv:8082/geoserver/wms</string>
<string>http://serv:8083/geoserver/wms</string>
<string>http://serv:8084/geoserver/wms</string>
<string>http://serv:8085/geoserver/wms</string>
</wmsUrl>

GetFeatureInfo Support

GeoWebCache per se is all about caching, hence by default it does not support any type of querying for the data. However we often want to be able to transparently switch WMS layers with tiled layers retaining the possibility to perform GetFeatureInfo  requests. Well, that is possible by properly configuring a <wmsLayer> as follows:<!– OPTIONAL Whether this layer will represent itself as queryable  in the getcapabilities document, and proxy getfeatureinfo requests to the backend server. The default is false. –>
<queryable>true</queryable>

Last but not least, we believe it is worth to mention that this work was performed via our GeoSolutions Enterprise Service. Therefore, if you’d like to know more about what we could achieve together, do not hesitate and get in touch with us!

The GeoSolutions team,