Select to view content in your preferred language

Custom layer is not being added - fetchTile not called

338
5
04-07-2025 04:44 AM
bearingwest
Emerging Contributor

I am working on a chrome extension which integrates with a site that uses esri. My extension adds custom layers. (This worked perfectly on 3.x...now they recently upgraded to 4.x, and I'm scrambling to get it working again.)

Here's my layer:

```

import { FetchTileRequest, postWindowMessage } from "@/types/messages";
import { BaseZoom, SystemZoom, TileInfo } from "@/types/layers";
import { loadModules } from 'esri-loader';
import BaseTileLayerProperties = __esri.BaseTileLayerProperties;

let BaseTileLayerClass: any = null;

export async function createCustomTileLayer(properties: BaseTileLayerProperties): Promise<__esri.Layer> {

// this is needed because direct import of BaseTileLayer doesn't work
// ("The item being added is not a Layer or a Promise that resolves to a Layer.")
if (!BaseTileLayerClass) {
    [BaseTileLayerClass] = await loadModules(['esri/layers/BaseTileLayer']);
}

const CustomTileLayer = BaseTileLayerClass.createSubclass({
    properties,

    getTileUrl: function(level: number, row: number, col: number): string {
        console.log("Getting tile URL", level, row, col);
        return `https://proxy.com/z=${level}&x=${col}&y=${row}`;
    },

    fetchTile: function(level: number, row: number, col: number): Promise<HTMLImageElement |              HTMLCanvasElement> {
        console.log("Fetching tile", level, row, col);

         ...

    };

    return new CustomTileLayer(properties);
}

```

I call this with:

```

async getLayer(type: LayerType, zoom: BaseZoom): Promise<__esri.Layer> {
    const config = this.layerConfig.getLayer(type, zoom);

    return createCustomTileLayer({
    title: translate(config.label),
    opacity: config.opacity,
    minScale: this.zoomToScale(config.minZoom),
    maxScale: this.zoomToScale(config.maxZoom),
    tileInfo: {
        dpi: 96,
        format: "png",
        size: [config.tileSystem.tileSize, config.tileSystem.tileSize],
        spatialReference: { wkid: 3857 },
        origin: {
             x: -20037508.342787,
             y: 20037508.342787,
            spatialReference: { wkid: 3857 }
        },
        lods: this.generateLODs(config.minZoom, config.maxZoom)
    }
});
}
```

And then finally:

```
this.window.map.add(esriLayer);
await esriLayer.load();
esriLayer.visible = true; 
```

I can see by dumping the layers on the map that the layer is added successfully. However, fetchTile is never called, so something in these settings is causing esri not to bother querying my layer.

Here's what I'm logging:

console.log("Map view:", {
scale: this.window.view.scale,
center: this.window.view.center.toJSON(),
zoom: this.window.view.zoom,
spatialReference: this.window.view.spatialReference.toJSON()
});

this.window.map.layers.forEach((layer) => {
console.log(`Layer: `, layer.title, layer.loaded, layer.visible, '||',
layer.fullExtent?.xmin, layer.fullExtent?.xmax,
layer.fullExtent?.ymin, layer.fullExtent?.ymax, '||',
'minScale' in layer ? layer.minScale : "",
'maxScale' in layer ? layer.maxScale : "",
layer.type, layer.opacity,
JSON.stringify('tileInfo' in layer && layer.tileInfo ? {
'dpi': (layer.tileInfo as TileInfo).dpi,
'format': (layer.tileInfo as TileInfo).format,
'size': (layer.tileInfo as TileInfo).size,
'lods': (layer.tileInfo as TileInfo).lods.map(lod => lod.level),
'origin': (layer.tileInfo as TileInfo).origin.toJSON(),
'spatialReference': (layer.tileInfo as TileInfo).spatialReference.toJSON(),
} : {})
)
});

 

Here's what my layer looks like:

Layer: My Custom Layer true true || -20037508.342787 20037508.34278 -20037508.34278 20037508.342787 || 73957190.94894437 70.53107352156103 base-tile 1 {"dpi":96,"format":"png","size":[256,256],"lods":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"origin":{"spatialReference":{"wkid":3857},"x":-20037508.342787,"y":20037508.342787},"spatialReference":{"wkid":3857}}

Here are some of the host site's layers, which work (if enabled):

Layer: Layer X true true || -1259927.5109 1261063.5594 -1201416.1471 2267331.5414 || 32000 0 map-image 0.8 {}
Layer: Layer Y true false || 286802.9026 658879.5627 889175.757200001 1241118.0719 || 16000 0 feature 0.8 {}
Layer: Layer Z true false || 121352.40221620945 824303.4391836704 871515.0272515337 1258709.6024793475 || 0 0 tile 1 {"dpi":96,"format":"mixed","size":[256,256],"lods":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],"origin":{"spatialReference":{"latestWkid":8908,"wkid":8908},"x":-5122600,"y":10001100},"spatialReference":{"latestWkid":8908,"wkid":8908}}


Obviously the spatial reference is different, but that's expected (I'm adding a mercator layer on top of CRTM05 layers, and expecting/hoping esri will do the job of handling this). If this is the issue, what's the better approach?

If that's not the problem, what else looks suspicious? 

Thanks

0 Kudos
5 Replies
Noah-Sager
Esri Regular Contributor

Hi @bearingwest, did you see our sample using a Custom TileLayer with 4.32? This might be helpful.
https://developers.arcgis.com/javascript/latest/sample-code/layers-custom-tilelayer/

0 Kudos
bearingwest
Emerging Contributor

Hey Noah, 

Yeah I worked from this initially, and then ran into some errors associated with typescript/esri (see my other active thread) which led to me adapting this sample.

I can confirm it's added to the Map - but for some reason the map is never calling fetchTile. 

What prerequisites have to be true in order for fetchTile to be true? I'm within range in terms of both scale and lod.

 

0 Kudos
bearingwest
Emerging Contributor

Noah - simple question, since I don't have access to the underlying code.

What prevents a layer from calling fetchTile? Can you look at the source code please? What steps does a layer need to pass in order for fetchTile to ultimately be called?

0 Kudos
bearingwest
Emerging Contributor

This is indeed the mixed spatialReferences, as I suggested in my question. I guess no esri developers read the details on this...disappointing.

With the help of Claude I stumbled on the underlying problem with:

```
webMercatorView.whenLayerView(esriLayer).then(layerView => {
console.log("Layer view created successfully:", layerView);
console.log("LayerView details:", {
visible: layerView.visible,
suspended: layerView.suspended,
updating: layerView.updating
});
```

My layer was always suspended. I had assumed ESRI would be capable of reprojecting layers in different spatialReferences, but apparently not(?)

So, I ended up creating a separate view for the web mercator layers, and adding it on top. That at least results in fetchTile being called. Now I am going to have to deal with linking movement and projections across the two views, but that's ok.

0 Kudos
GregoryHyson
Emerging Contributor

Hi bearingwest.
I have experienced the fetchTile failing to call as well, and it was a symptom of improper spatial reference handling. My hypothesis on it was the since the MapView doesn't have the lod's specified appropriately, it doesn't know how to associate the scales it is currently at with a tile to request from the server.

I raised a ticket and discussed spatial references handling on CustomTileLayers.
Here is the ticket: https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/custom-tilelayers-created-with-ba...

All of this is to say: If you explicitly set the spatialReference, tileInfo (make sure you include the lods in your constructor) and the fullExtent on CustomTileLayer, then when you add it to the mapView you also add the spatialReference there, it may solve your issue.

I was provided a CodePen in the ticket mentioned that may help provide clarity on this.

Hope this helps.

0 Kudos