Select to view content in your preferred language

Custom TileLayers created with BaseTileLayer.createSubclass do not handle alternate Spatial Reference's

514
7
Jump to solution
04-13-2025 03:24 PM
GregoryHyson
Emerging Contributor
Greetings!
During my usage of subclasses of BaseTileLayer, I have found that some spatial references are not being handled appropriately, particularly when creating the mapView with a TileLayer.
I have included a codepen that illustrates this behaviour in a minimal application, you can comment out the code defining currentLayer:

// Custom TileLayer STARTS HERE
const currentLayer = new SimpleLayer({
  urlTemplate: nztmBasemapTileTemplate,
});
// ENDS HERE

// TileLayer Definition STARTS HERE
const currentLayer = new TileLayer({
  url: nztmBasemap,
});
// ENDS HERE

The console logs show the issue well. It seems that the spatial reference is not being detected by the Basemap and MapView from the customized TileLayer. This seems to be preventing appropriate display of the TileLayer.

All of the above seems to me to show a bug in BaseTileLayer.

As an aside, I did manange to get some more SpatialReference's working in the CustomTileLayer, by manually adding spatialReference and tileInfo to the constructor, but it seems to me that the map servers response should be digested such that the spatial reference and tile info are automatically instantiated.

See below what this would look like:

const spatialReference = new SpatialReference({ wkid : mapServerResponse.wkid });

new CustomTileLayer({
  urlTemplate: urlTemplate,
  spatialReference : spatialReference,
  tileInfo: new TileInfo({ spatialReference: spatialReference }),
});

This works for fixing NZTM(2193), but failed to fix it for other spatial references. I presume this is because certain required parameters are not being added to the SpatialReference, TileInfo or other relevant properties on the TileLayer.

Couple of requests/suggestions from this:
- If I'm missing something that will make this work for CustomTileLayers, what is it?
- If there is a fix for this involving providing the right parameters to the Custom TileLayer to be able to handle all SpatialReferences, then having that documented somewhere / provided here, that would be mint!


Cheers,
Greg.
0 Kudos
1 Solution

Accepted Solutions
UndralBatsukh
Esri Regular Contributor

Hi there, 

Thanks for the clarification! I should’ve read the post a bit more carefully.

You're almost there! BaseTileLayer and WebTileLayer are preconfigured to use the Web Mercator spatial reference. To align your BaseTileLayer with your service, you'll need to explicitly set the tileInfo, spatialReference, and fullExtent properties in the custom layer's constructor.

This CodePen demonstrates that approach: https://codepen.io/U_B_U/pen/yyyBzMO?editors=1000

Alternatively, if you want to avoid creating a custom layer, you can use WebTileLayer directly as shown below:

const tiledLayer = new WebTileLayer({
  urlTemplate: "https://services1.arcgisonline.co.nz/arcgis/rest/services/Imagery/newzealand/MapServer/tile/{z}/{y}/{x}",
  tileInfo,
  fullExtent,
  spatialReference
});

 

View solution in original post

0 Kudos
7 Replies
UndralBatsukh
Esri Regular Contributor

Hi there, 

I believe you're talking about projecting your TileLayer into different spatial references. Please let me know if I’ve misunderstood.

It's important to note that TileLayer cannot be reprojected on the client side. This is outlined in MapView.spatialReference doc, specifically, it states that in order for
TileLayer and VectorTileLayer to render correctly as part of a basemap, the MapView's spatialReference must match the tileInfo.spatialReference of those layers.

0 Kudos
GregoryHyson
Emerging Contributor
Thanks for the reply, but I think I failed to convey the issue fully.

The server I am referencing is in wkid : 2193.
I am not providing a spatial reference value for the TileLayer, Basemap or MapView.

Here's an explanation of what I am seeing:
1. I create a TileLayer for an NZTM (wkid: 2193) Basemap.
2. I add it to a BaseMap
3. I add the BaseMap as the input parameter to a MapView's layers.
4. The spatial reference is populated as 2193 in the TileLayer, the BaseMap and the MapView, and as such the map displays.

Alternately:
1. I create a CustomTileLayer for an NZTM (wkid: 2193) Basemap
2. I add it to a BaseMap
3. I add the BaseMap as the input parameter to a MapView's layers, also without an explicit Spatial Reference specified.
4. The spatial reference is defaulted to 102100, and the data doesn't align with the Spatial Reference so the map fails to display.

If you want to see this happening in my CodePen, you can comment out the line defining currentLayer with the BaseTileLayer implementation, then let the application run and open the console.
Then you can comment out the line definining currentLayer with the TileLayer implementation, and uncomment the BaseTileLayer currentLayer definition.
When you check the console again you will see the spatial reference is defaulted to 102100.

Thus what I am seeing is the TileLayer is correctly finding the spatial reference specified in the map server definition and propagating this through to the BaseMap, and then the MapView, and the CustomTileLayer failing to recognise the spatial reference specified in the map server definition.
0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

Thanks for the clarification! I should’ve read the post a bit more carefully.

You're almost there! BaseTileLayer and WebTileLayer are preconfigured to use the Web Mercator spatial reference. To align your BaseTileLayer with your service, you'll need to explicitly set the tileInfo, spatialReference, and fullExtent properties in the custom layer's constructor.

This CodePen demonstrates that approach: https://codepen.io/U_B_U/pen/yyyBzMO?editors=1000

Alternatively, if you want to avoid creating a custom layer, you can use WebTileLayer directly as shown below:

const tiledLayer = new WebTileLayer({
  urlTemplate: "https://services1.arcgisonline.co.nz/arcgis/rest/services/Imagery/newzealand/MapServer/tile/{z}/{y}/{x}",
  tileInfo,
  fullExtent,
  spatialReference
});

 

0 Kudos
GregoryHyson
Emerging Contributor

Thanks for the replies Undral.

I'm happy to accept this post as the solution, but it would be great if either:
 - this could be added to the Documentation for the BaseTileLayer
or
- the BaseTileLayer could interpret this information from the server specified, as it does in TileLayer.

Speaking to whether this should be a feature of BaseTileLayer: While I have solved my issue, in order to handle different basemaps I need to query and digest the content of the mapServerResponse, which I feel like should be handled by the BaseTileLayer class.

Speaking to whether this should be documented: In the integration I am doing I had gotten to the TileInfo and SpatialReference addition, but that was through stabbing in the dark and getting lucky. I was just missing using the constructor for creating the TileInfo and specifying the lod's. After adding the tileInfo, spatialReference and fullExtent to the constructor, it seems to be working for all spatialReferences without issue.

If: "To align your BaseTileLayer with your service, you'll need to explicitly set the tileInfospatialReference, and fullExtent properties in the custom layer's constructor." was specified in the BaseTileLayer documentation, I am sure I would have found it a lot easier to get this working.

Thanks again for your time and assistance in solving this issue.
Cheers,
Greg.

0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

BaseTileLayer is designed to be a generic tile layer and is not limited to working with ArcGIS map services — that's what TileLayer is for. Because of this, it doesn’t make assumptions about the type of data it can retrieve from various sources.

Could you share a bit more about why you're creating a custom BaseTileLayer? Understanding your use case will help us improve support and documentation.

We'll update the BaseTileLayer documentation to reflect this workflow more clearly.

0 Kudos
GregoryHyson
Emerging Contributor

Okay, that makes sense.  
Thanks for actioning the documentation change, I think in light of the use case outlined that would be a welcome addition to the documentation.

We are using BaseTileLayer to cater for Offline and Hybrid Online/Offline basemaps, allowing us to take Basemaps offline for use without internet on a device. 
The main value it provides for us is the ability to override fetchTile, which we can then point to our local database, and failing that back out to the TileLayer URL.


We create three different definitions for a TileLayer.
 - An OnlineTileLayer, which calls the unextended fetchTile
 - An OfflineTileLayer, which makes a call to the devices database, to check if there is a tile at the specified row, col and level, and display it if there is.
 - A HybridTileLayer, which makes an offline call and either uses that value on success, or on failure it makes an online call, which it will then optionally save to the database for later offline display.

0 Kudos
UndralBatsukh
Esri Regular Contributor

Hi there, 

The BaseTileLayer overview doc will be updated with the following info in version 4.33. 

Screenshot 2025-04-22 at 2.03.54 PM.png

0 Kudos