I'm finding an unexpected behavior when setting alignTopLeft=true at geowebcache.xml for a gridSet. When I am navigating on the map within the same cache level everything works fine (even zooming in/out works fine). However, when the map changes to a different cache level, I am taken vertically to a different location, as if there was a different y-offset for each cache level. This behavior has been observed with both OpenLayers and QGIS. I believe it has to do with the fact that when alignTopLeft=false you get different tile origins for each cache level, while when alignTopLeft=true, despite the fact that now you get the same tile origin for all cache levels, somehow the service is internally still using different tile origins and it doesn't work properly is you use the same single one. More details below.

At GeoServer (both 2.13.1 and 2.15), I have configured tile caching on a layer with the formats "image/png" and "application/x-protobuf;type=mapbox-vector", and assigned the following gridSet to it:


When requesting the capabilities of the GeoWebCache WMTS service, I am getting the following TileMatrixSet, which has a different tile origin (TopLeftCorner) for each cache level.

    <TopLeftCorner>421849.8127979373 1088692.0</TopLeftCorner>
    <TopLeftCorner>421849.8127979373 981172.0</TopLeftCorner>
    <TopLeftCorner>421849.8127979373 970420.0</TopLeftCorner>

With the configuration above, everything works fine. I get both WMTS and Vector Tile layers working fine on OpenLayers, and the WMTS service works fine in QGIS. However, having multiple tile origins causes me other issues and I'm trying to configure/force the use of a single tile origin (same for all cache levels). With that in mind, I changed to


on geowebcache.xml, and that successfully makes all tile origins (TopLeftCorners) be the same on the WMTS capabilities description, that is the top left corner of the SRS: [421849.8127979373 968015.3873920501]. However, with this new configuration the layer doesn't behave properly when displayed neither in OpenLayers nor in QGIS, where you find different y-offsets for each cache level while navigating on the map, and you are taken vertically to different locations whenever the cache level changes.

I can confirm that the vector tile layer works again properly in OpenLayers if I use the same collection of different tile origins associated to alignTopLeft=false instead of the same tile origin associated to alignTopLeft=true, even when alignTopLeft=true is set. So, it looks like despite alignTopLeft=true makes all tile origins be the same, in reality the service won't work properly unless you still use the collection of different tile origins obtained when alignTopLeft=false.

Can someone help, please?

2 Answers 2


Just in case it can be of help I will share my findings. The use of alignTopLeft=true doesn't change the fact that GeoWebCache (or at least TMS) has Y coordinates growing upwards (moving towards north). The only difference is that because the tiles are aligned at the top-left corner, there will be multiple tile origins at the bottom-left corner rather than a single origin.

So, independently of the value of alignTopLeft, GeoWebCache/TMS have their tile origin(s) at the bottom-left corner and Y coordinates growing upwards, while OpenLayers assumes the tile origin(s) is/are at the top-left corner and uses Y coordinates growing downwards. In order to make OpenLayers work with just one tile origin we need to appropriately transform the XYZ tile coordinates given by OpenLayers to the corresponding tile coordinates needed by GeoWebCache/TMS.

If you use alignTopLeft=true, then in OpenLayers you need to configure a single tile origin located at the top-left corner and specify a url for TMS flipped vertically (either use -y or append ?flipY=true). For example:

url: "http://GEOSERVER_BASE_URL/gwc/service/tms/1.0.0/LAYER_NAME@GRIDSET_NAME@pbf/{z}/{x}/{y}.pbf?flipY=true"

If you use alignTopLeft=false, then in OpenLayers you need to configure a single tile origin located at the bottom-left corner and specify a tileUrlFunction that allows you to apply the appropriate transform to the Y coordinate, in this case Y => -Y - 1. For example:

tileUrlFunction: (tileCoord, pixelRatio, projection) => {
  return `http://GEOSERVER_BASE_URL/gwc/service/tms/1.0.0/LAYER_NAME@GRIDSET_NAME@pbf/${tileCoord[0]}/${tileCoord[1]}/${-tileCoord[2]-1}.pbf`;

I suspect this may be related to this open issue: geowebcache/issues/612

GitHub-user wonder-sk attributed the differences in the Y-value of TopLeftCorner to a piece of code at GridSet.java#L356 which is rounding the Y-value (but only if it is within 0.5% of an integer value).

The issue discussion also contains a workaround suggested by user MiquelRS:

The solution consisted in creating a gridset with Width and Heigh multiple of tile size in px. So, if using 256px tiles, modifying the gridset size so when dividing width of heigh by 256 the result is number with no fraction (integer). This would align tiles of different zoom levels to the exactly the same position.

  • Thanks for your reply, MattiasSp. I'm calculating my own tile origins different from the default ones to prevent the issue of rounding when closer to 5% (basically I'm using the same code without the rounding part). The problem I have is that I would like to use a single tile origin located at the top left corner, but the property "alignTopLeft" doesn't seem to have any impact.
    – J. L.
    Mar 9, 2020 at 11:14
  • I guess my problem is similar to this: geoserver-users.narkive.com/mRusc4au/…
    – J. L.
    Mar 9, 2020 at 12:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.