0

I am integrating WMTS in a Java application using GeoTools. As it is easier to integrate, I am fetching tile by tile. This works fine for the following server/capabilities: https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/WMTS/1.0.0/WMTSCapabilities.xml (As layer I am using USGSImageryTopo and as Tile Matrix Set default028mm.)

So far so good, but if I use the following layer (https://gdz.bkg.bund.de/index.php/default/wmts-basemapde-schummerung-wmts-basemapde-schummerung.html), my program does not show the expected layer.

MRE (without Maven):

package test;

import java.net.*;

import org.geotools.ows.wmts.*;
import org.geotools.ows.wmts.model.*;
import org.geotools.ows.wmts.request.*;

public class GeoToolsWmtsTest
{

  public static void main(String[] args) throws Exception
  {
    URL url = new URL("https://sgx.geodatenzentrum.de/wmts_basemapde_schummerung/1.0.0/WMTSCapabilities.xml");
    WebMapTileServer wmts = new WebMapTileServer(url);
    WMTSCapabilities capabilities = wmts.getCapabilities();
    WMTSLayer layer = capabilities.getLayer("de_basemapde_web_raster_hillshade");
    TileMatrixSet matrixSet = capabilities.getMatrixSet("GLOBAL_WEBMERCATOR");
    
    getTileRequest(wmts, layer, matrixSet, true);
    getTileRequest(wmts, layer, matrixSet, false);
  }

  private static void getTileRequest(WebMapTileServer wmts, WMTSLayer layer, TileMatrixSet matrixSet, boolean multi)
  {
    GetTileRequest request = wmts.createGetTileRequest(multi);
    request.setLayer(layer);
    request.setFormat(layer.getFormats().get(0));
    request.setTileMatrixSet(matrixSet.getIdentifier());
    request.setTileMatrix("11");
    request.setTileRow(693);
    request.setTileCol(1094);
    
    System.out.println("Expected (from QGis): https://sgx.geodatenzentrum.de/wmts_basemapde_schummerung/tile/1.0.0/de_basemapde_web_raster_hillshade/default/GLOBAL_WEBMERCATOR/11/693/1084.png");
    System.out.println(request.getFinalURL());
  }


}


The result of this code is:

Expected: https://sgx.geodatenzentrum.de/wmts_basemapde_schummerung/tile/1.0.0/de_basemapde_web_raster_hillshade/default/GLOBAL_WEBMERCATOR/11/693/1084.png
https://basemap.de?REQUEST=GetTile&type=REST&VERSION=1.0.0&SERVICE=WMTS
Exception in thread "main" java.lang.NullPointerException
    at org.geotools.data.ows.AbstractRequest.<init>(AbstractRequest.java:76)
    at org.geotools.ows.wmts.request.AbstractWMTSRequest.<init>(AbstractWMTSRequest.java:33)
    at org.geotools.ows.wmts.request.AbstractGetTileRequest.<init>(AbstractGetTileRequest.java:114)
    at org.geotools.ows.wmts.WMTSSpecification$GetSingleTileRequest.<init>(WMTSSpecification.java:270)
    at org.geotools.ows.wmts.WMTSSpecification$GetRestTileRequest.<init>(WMTSSpecification.java:334)
    at org.geotools.ows.wmts.WebMapTileServer.createGetTileRequest(WebMapTileServer.java:296)
    at test.GeoToolsWmtsTest.getTileRequest(GeoToolsWmtsTest.java:26)
    at test.GeoToolsWmtsTest.main(GeoToolsWmtsTest.java:21)

I retrieved the expected URL from QGis. The layer works fine in QGis.

Multi-Request

I am not sure if it is even allowed to set tile row and column here. But as the base URL is completely different as the expected one, that is probably not the problem.

Single-Request

This is what I am using in my actual program, if you debug the NPE you will see that OperationType getTile = getCapabilities().getRequest().getGetTile() is null. And indeed the operation GetTile is missing in the capabilities.

So now my actual question(s) are:

  • Am I using GeoTools wrong?
  • Is the capabilities broken? (But the Layer is shown in QGis)
  • Is it a bug in GeoTools?

Result of debug session after the comment by Ian Turton. There is quite a lot going, so I might have overseen something. But I think there are two bugs, one in the multi-request case and one in the single-request case.

Multi-Request

When the MRE uses the nationalmap-layer the generated URL is correct for the single-tile request. But wrong for the multi-tile request.

  public static void main(String[] args) throws Exception
  {    
    URL url = new URL("https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/WMTS/1.0.0/WMTSCapabilities.xml");
    WebMapTileServer wmts = new WebMapTileServer(url);
    WMTSCapabilities capabilities = wmts.getCapabilities();
    WMTSLayer layer = capabilities.getLayer("USGSImageryTopo");
    TileMatrixSet matrixSet = capabilities.getMatrixSet("default028mm");
    
    getTileRequest(wmts, layer, matrixSet, true);
    getTileRequest(wmts, layer, matrixSet, false);
  }

  private static void getTileRequest(WebMapTileServer wmts, WMTSLayer layer, TileMatrixSet matrixSet, boolean multi)
  {
    GetTileRequest request = wmts.createGetTileRequest(multi);
    request.setLayer(layer);
    request.setFormat(layer.getFormats().get(0));
    request.setTileMatrixSet(matrixSet.getIdentifier());
    request.setTileMatrix("11");
    request.setTileRow(693);
    request.setTileCol(1094);
    
    System.out.println(request.getFinalURL());
  }

Result:

https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/WMTS/1.0.0/WMTSCapabilities.xml?REQUEST=GetTile&type=REST&VERSION=1.0.0&SERVICE=WMTS
https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/WMTS/tile/1.0.0/USGSImageryTopo/default/default028mm/11/693/1094

The multi-request (first) URL will simply return the capabilities.

I guess that the problem is that in the multi-request case a instance of GetMultiTileRequest is used. This class does not override getFinalURL(), therefore the generic AbstractRequest.getFinalURL() is used. This method does not distinguish between REST and KVP as these concepts are (I guess) WMTS specific.

side note

What should a multi-request even do, if TileRow and TileCol should be inserted in the URL template?

Single-Request

In this case, I think that the lines from the multi-request are just missing:

if (url == null) {
    type = WMTSServiceType.REST;
    url = getCapabilities().getService().getOnlineResource();
}

This would avoid the NPE and an instance of GetRestTileRequest would be used. Which overrides the getFinalURL() method and makes use of the Template URL from the given layer.

6
  • 1
    There are two possible ways to access the tiles in WMTS. One is with the GetTile request and another is to use a RESTful request. The second server supports the latter with <ResourceURL format="image/png" resourceType="tile" template="https://sgx.geodatenzentrum.de/wmts_basemapde_schummerung/tile/1.0.0/de_basemapde_web_raster_combshade/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png"/>.
    – user30184
    Commented Mar 13, 2023 at 11:34
  • 1
    createGetTileRequaest is supposed to handle this type of situation - see github.com/geotools/geotools/blob/main/modules/extension/wmts/… - can you step through there and see if you can see what's up. It smells like a bug to me
    – Ian Turton
    Commented Mar 13, 2023 at 11:54
  • I added a section to the question after debugging.
    – gillesB
    Commented Mar 13, 2023 at 13:49
  • 1
    Open a new one with a link to that closed issue and I'll try to take a look at the weekend
    – Ian Turton
    Commented Mar 14, 2023 at 10:07
  • 1
    fixed at github.com/geotools/geotools/pull/4234
    – Ian Turton
    Commented Mar 17, 2023 at 15:32

1 Answer 1

1

To help anyone who finds this question in the future, GetTileRequest request = wmts.createGetTileRequest(true); (the multi request in the question) should only be used if you are requesting a collection of tiles by specifying the bounding box of the area to be mapped. If (as the OP is) you want a single tile then you should use GetTileRequest request = wmts.createGetTileRequest(false); and specify the row and column of the tile matrix required.

All the other issues were a GeoTools bug and are now fixed.

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.