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.
<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"/>
.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