How to create a custom tile layer (BaseTileLayer) using 512x512 tiles?

1131
4
Jump to solution
07-22-2022 08:34 AM
RyanSutcliffe
Occasional Contributor II

In an ArcGIS JavaScript API project I am working on, we have a source dataset from an API that supports 512x512 as well as 256x256 tiling schemes. I'm keen to test if the 512x512 scheme improves performance on large and high resolution devices but cannot figure out how to set it up with the ArcGIS JavaScript API's BaseTileLayer.

I've got the 256x256 version working in an approach akin to the ESRI example here for a custom BaseTileLayer in an ArcGIS API map. 

- There is no mention of tiling scheme support or limitations on the BaseTileLayer API page, but looking at the API page of the TileLayer, there seems to be support for 512x512 tiling schemes, so I assume this extends to the BaseTileLayer as well.

- I see that there is a TileInfo property on the BaseTileLayer which has a size property, although its not 100% clear to me how this can be set.

What I Tried

I've tried using the 512x512 source dataset in my layer. It draws but with artifacts that suggest something is wrong with the tiling. I've then tried modifying the tileInfo property on the baseTileLayer after the layer is created like so:

const CustomTileLayer = baseTileLayer.createSubClass({
...
})

let myCustomLayer = new CustomTileLayer({
...
});

myCustomLayer.tileInfo.size = 512;

This will cause errors and nothing to draw. On a serverside rendered layer, the esriRequest within the fetchTile method will fail with 'unable to load [url]'. On a clientside rendered layer, the fail will happen when I try to read the data from the fetched ArrayBuffer stream of data:

const response = await esriRequest(url, {
	method: "GET",
	referrer: "origin",
	signal: options && options.signal,
	headers: {
	  Accept: "*/*",
	},
	responseType: "array-buffer",
  });

  const buffer = response.data;
  // buffer.bytelength = 0?
  
  //consuming buffer fails.

Can anyone spot what I am doing wrong or missing? What is the trick to configure a baseTileLayer with a custom 512x512 tiling scheme correctly?

Unfortunately the source dataset I am consuming is not public so I can't share any codePen examples of what I've tried or what I'm doing with 256x256 default approach? 

0 Kudos
1 Solution

Accepted Solutions
UndralBatsukh
Esri Regular Contributor

Hi there, 

You cannot directly set the TileInfo.size as you are doing. It needs to be set in the custom layer constructor as shown below: 

 

const TintLayer = BaseTileLayer.createSubclass({
  constructor() {
    this.tileInfo = TileInfo.create({
      size: 512,
      spatialReference: { wkid: 102100 }
    });
  },
...

 

Please take a look at this codepen for how it is done. 

Here a link to the TileInfo.create() method. 

View solution in original post

4 Replies
UndralBatsukh
Esri Regular Contributor

Hi there, 

You cannot directly set the TileInfo.size as you are doing. It needs to be set in the custom layer constructor as shown below: 

 

const TintLayer = BaseTileLayer.createSubclass({
  constructor() {
    this.tileInfo = TileInfo.create({
      size: 512,
      spatialReference: { wkid: 102100 }
    });
  },
...

 

Please take a look at this codepen for how it is done. 

Here a link to the TileInfo.create() method. 

RyanSutcliffe
Occasional Contributor II

Thanks for your help and example. This worked for me as well just now. 
Looking at this more closely, I'm trying to understand. How do we know, from the API pages, whether a property is settable or whether it can only be set up as part of the constructor? I know that certain properties are "read-only", so they can not be changed at all. But how do I know if a property can be configured in the constructor only?

0 Kudos
RyanSutcliffe
Occasional Contributor II

Chatting with a support rep at ESRI Canada he pointed out that this is because we are using the 'createSubClass' method on the BaseTileLayer class to instantiate a new instance which is a (subclass of?) Accessor. Looking at that class we can see properties are not adjusted directly but via setters and getters.

So we could change the tileInfo size property via something like:

myCustomTileLayer.set('tileInfo.size', 512)

However logically it makes more sense to set up this in the constructor as above. I can't think of a valid use case where you'd want to switch the tiling scheme of a layer after it was already in use.

Here is  a codePen example an ESRI Canada rep set up for me showing, just for interest, that using set() works as well (see line 145).

 

0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

You have to set the tileInfo in the layer constructor. The tileInfo.size is used to derive the layer's tileInfo.lods  and eventually MapView.constraints.LODs (if it is base or only layer). Try zooming in and out in your codepen you can see that LODS calculations are incorrect (visually).