Why is WebTiledLayer performance horrible?

06-12-2018 09:39 AM
Occasional Contributor

Why is WebTiledLayer being super slow on purpose?

Both I and a colleague see this problem.

Using WebTiledLayer with some custom urls.

For control group, we used World Topographic Map.

Yes, I know this could be done with a different class also, but just for comparison.

We also used other servers from Canada, Australia and Tasmania.

We tried this simple test:

Zoom in by several steps quickly from zoom 7 to 15

World Topographic Map worked very fast. You can see the difference fairly soon.

We notice via the logs that World Topographic Maps is download all levels between 7 and 15, but it does so quickly.

06-12 08:22:04.605 511-1749/.... key:  11,344,727 url:  https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/11/727/344/
06-12 08:22:04.605 511-1768/... key:  11,344,730 url:  https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/11/730/344/
06-12 08:22:04.606 511-1800/...: key:  11,343,731 url:  https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/11/731/343/

You notice there is only milliseconds or less between requests.

We tried with another server template from Toporama WMS in Canada. (Yes, I know that there are WMS classes too, but I am making a comparison).

You are waiting for five or ten minutes for the screen to refresh.

It is still downloading all the levels - but taking its time. 

06-12 08:50:31.190 511-29904/... key:  9,106,160 url:  http://maps.geogratis.gc.ca/wms/toporama_en?version=1.1.1&request=GetMap&format=image/png&bgcolor=0x...
06-12 08:50:32.180 511-29907/... key:  9,105,158 url:  http://maps.geogratis.gc.ca/wms/toporama_en?version=1.1.1&request=GetMap&format=image/png&bgcolor=0x...
06-12 08:50:33.145 511-29909/... key:  9,106,157 url:  http://maps.geogratis.gc.ca/wms/toporama_en?version=1.1.1&request=GetMap&format=image/png&bgcolor=0x...
06-12 08:50:35.675 511-29910/... key:  9,107,157 url:  http://maps.geogratis.gc.ca/wms/toporama_en?version=1.1.1&request=GetMap&format=image/png&bgcolor=0x...

As you see, it waits between one and five seconds before it even makes a request.

Can we blame the server? No, because the SDK isn't trying too hard.

Slow Web Tiled Layer

A colleague in Canada has the same performance.

Somehow, our colleague in Vietnam did not see this problem, even though he is much farther from the server.

I tried it with many other servers that were pretiled, including some obviously using MapServer or ImageServer:


It appears you have some code like this:

foreach(tile in tilesNeeded)


   //we want to make sure anyone not using ArcGISOnline looks bad.

   if( not from ArcGISOnline)


      //impose a meaningless delay.

      wait one to five seconds;




What the heck is going on?

0 Kudos
7 Replies
Regular Contributor II

Hi Nathan.

Thanks for reporting this.

I assume you see this behavior with 100.2.1?

We'll take a look.

Has this been reported to Esri Support by chance?

0 Kudos
Occasional Contributor

Yes, this is 100.2.1.

I have a case in support with this information.

0 Kudos
Occasional Contributor

As a slight correction.

WebTiledlayer does not allow you to overrride getTileUrl and expect it to be called.

We are using a subclass of ServiceImageTiledLayer.

I have produced a simple example using your WebTiledLayer sample. The

If you change this line of code to use WebTiledLayer instead of my custom class, performance is just fine.

Using my custom class inherited from ServiceImageTiledLayer  - well, I am still waiting for the map to refresh after 10 minutes. In addition, the runtime thread pool of ArcGIS gets all used up, so features like place search suggestion don't work.

final MyWebTiledLayer webTiledLayer= new MyWebTiledLayer(templateUri);

I have escalated the case with Esri support, but they are unlikely to find a solution without help from developers.

While this template could be used with either WebTiledLayer or a custom class, there are many that cannot. We use up to 60 template urls in our application.

Web Tiled Layer can only handle {row} {col} {level} as tags.

Notably it cannot handle TMS server, just because the y is upside down from your {row} tag.

It cannot handle quadkeys like in Bing Urls. I am aware there is a Bing Maps layer, but it only has three styles. It could not handle, for instance, the Ordnance Survey style for the UK.

It can't handle a bounding box query to create tiles from a WMS server that supports 900913 projection.

/* Copyright 2017 Esri
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and
* limitations under the License.

package com.esri.arcgisruntime.sample.webtiledlayer;

import java.util.Arrays;
import java.util.List;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.esri.arcgisruntime.arcgisservices.TileInfo;
import com.esri.arcgisruntime.layers.WebTiledLayer;
import com.esri.arcgisruntime.loadable.LoadStatus;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.mapping.Viewpoint;
import com.esri.arcgisruntime.mapping.view.MapView;

public class MainActivity extends AppCompatActivity {

  private MapView mMapView;

  protected void onCreate(Bundle savedInstanceState) {

    // access MapView from layout
    mMapView = (MapView) findViewById(R.id.mapView);

    // list of subdomains
    List<String> subDomains = Arrays.asList("a", "b", "c", "d");
    // url pattern
    String templateUri = "http://services.thelist.tas.gov.au/arcgis/rest/services/Basemaps/Topographic/ImageServer/tile/{level}/{row}/{col}/";

    // webtile layer
    final MyWebTiledLayer webTiledLayer= new MyWebTiledLayer(templateUri);
    webTiledLayer.addDoneLoadingListener(new Runnable() {
      public void run() {
        if (webTiledLayer.getLoadStatus() == LoadStatus.LOADED) {
          // use webtile layer as Basemap
          ArcGISMap map = new ArcGISMap(new Basemap(webTiledLayer));
          map.setInitialViewpoint(new Viewpoint(-42.106967,147.097578,577791));
          TileInfo tinfo = webTiledLayer.getTileInfo();
          Log.i("webtiledlayer","web tileinfo: " + tinfo);

          // custom attributes
          webTiledLayer.setAttribution("Map tiles by <a href=\"http://stamen.com/\">Stamen Design</a>, " +
              "under <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a>. " +
              "Data by <a href=\"http://openstreetmap.org/\">OpenStreetMap</a>, " +
              "under <a href=\"http://creativecommons.org/licenses/by-sa/3.0\">CC BY SA</a>.");

  protected void onPause() {
    // pause MapView

  protected void onResume() {
    // resume MapView

  protected void onDestroy() {
    // dispose MapView

package com.esri.arcgisruntime.sample.webtiledlayer;

import android.util.Log;

import com.esri.arcgisruntime.arcgisservices.LevelOfDetail;
import com.esri.arcgisruntime.arcgisservices.TileInfo;
import com.esri.arcgisruntime.data.TileKey;
import com.esri.arcgisruntime.geometry.Envelope;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.SpatialReferences;
import com.esri.arcgisruntime.layers.ServiceImageTiledLayer;
import com.esri.arcgisruntime.layers.WebTiledLayer;

import java.util.ArrayList;

* Created by valuedcustomer2 on 6/15/18.

public class MyWebTiledLayer extends ServiceImageTiledLayer {

    private static final String TAG = MyWebTiledLayer.class.getSimpleName();

    String baseurl="default";

    public MyWebTiledLayer(String templateUri) {
        super(CreateTileInfo(1,15,templateUri), new Envelope(-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244, SpatialReferences.getWebMercator()));
        baseurl = templateUri;

        TileInfo ours = getTileInfo();
        Log.i(TAG,"ours: " + ours.getCompressionQuality() + ", " + ours.getDpi() + ", " + ours.getFormat()+ ", "
                + ours.getLevelsOfDetail() + ", " + ours.getOrigin() + ", " + ours.getSpatialReference() + ", " + ours.getTileHeight() + ", " + ours.getTileWidth()) ;

        Log.i(TAG, "end of constructor");


    public String getUri() {
        return baseurl;

    private static final int TILESIZE = 256;
    private static final double INITIALRESOLUTION = 2 * Math.PI * 6378137 / TILESIZE;

    static public double zresolution(int zoom) {
        return INITIALRESOLUTION / Math.pow(2, zoom);

    private static TileInfo CreateTileInfo(int minZoom, int maxZoom, String baseurl)
        WebTiledLayer wlayer = new WebTiledLayer(baseurl);

        TileInfo theirs =wlayer.getTileInfo();

        Log.i(TAG, "their extent: " + wlayer.getFullExtent());

        Log.i(TAG,"theirs: " + theirs.getCompressionQuality() + ", " + theirs.getDpi() + ", " + theirs.getFormat()+ ", "
                + theirs.getLevelsOfDetail() + ", " + theirs.getOrigin() + ", " + theirs.getSpatialReference() + ", " + theirs.getTileHeight() + ", " + theirs.getTileWidth()) ;

        int dpi = 96;
        ArrayList<LevelOfDetail> levels = new ArrayList<>();
        for (int i = minZoom; i <= maxZoom; i++)
            double resolution = zresolution(i);
            double scale = resolution * dpi * 39.37;
            LevelOfDetail l = new LevelOfDetail(i, resolution, scale);
            LevelOfDetail t = theirs.getLevelsOfDetail().get(i);
            Log.i(TAG," our LOD("+ i +"," + resolution + "," + scale + ")");
            Log.i(TAG,"their LOD("+ t.getLevel() +"," + t.getResolution() + "," + t.getScale() + ")");

        return theirs;
        //return new TileInfo(dpi, TileInfo.ImageFormat.PNG, levels, new Point(-20037508.342789244, 20037508.342789244, SpatialReferences.getWebMercator()), SpatialReferences.getWebMercator(), 256, 256);

    protected String getTileUrl(TileKey tileKey) {

        String tileurl = baseurl.replace("{level}",String.valueOf(tileKey.getLevel())).replace("{col}",String.valueOf(tileKey.getColumn())).replace("{row}",String.valueOf(tileKey.getRow()));
        Log.d(TAG,"Url = " +tileurl);
        return tileurl;

    public String getId() {
        return super.getId();

    public void setId(String id) {
0 Kudos
Regular Contributor II

Thank you for this, Nathan. Looking into it now....

0 Kudos
Occasional Contributor

Thanks. I'd hate to have to resort to proxies or something to work around the Url pattern limitations of WebTiledLayer and the performance limitations of the ServiceImageTiledLayer.

0 Kudos
Regular Contributor II

Hi Nathan.

Just to give you an update: we've addressed this issue and it will be released as part of Update 4, October of this year.

Thanks for your patience and reporting on this!

0 Kudos
Occasional Contributor
I guess that didn't happen because the release notes still list it as a known issue.
  • BUG-000115165 Poor WebTiledLayer performance when deriving the class from a custom ServiceImageTiledLayer.
0 Kudos