I would like to download raster image data in a leaflet / esri-leaflet context. To provide a little backround info: When you add an esri-leaflet raster layer to a leaflet map, you can see that on map move end, a request is made to the arcgis layer service url. The request contains the bounds of the map, and an image of just those bounds are returned and layed over the map as an image overlay.

I would like to tweak this process. (I am reproducing this behavior on a node server, without a map.) Instead of requesting one single image that covers the whole map bounds, I want to download a series of tiles, which when put together, make up the same image that covers the whole bounds of the map. I would like these tiles to have the same bounds as as the tiles of a standard slippy map tile layer (assuming standard web mercator projection).

Let's say I have a function already written that takes in a latLngBounds, and returned the fetched image for those bounds. (Its too complicated to post it all here, as it is reconstructed from a lot of esri-leaflet's source code). We'll call it fetchImageFromBounds(bounds: L.LatLngBounds).

For a given map latLngBounds, I can get all the tiles contained within those bounds using xyz-affair:

import xyz from 'xyz-affair';

const scale = 10;

const fullExtentBounds = [topLeft, bottomRight];
const tileCoords = xyz(fullExtentBounds, scale);

Now I would like to write a function that returns the latLngBounds for each of the tile coordinates:

function tileCoordToBounds(coord){
  const {x, y} = coord;

  const topLeftLayerPoint = new L.Point(
    X * tileSize, 
    Y * tileSize

  const bottomRightLayerPoint = new L.Point(
    (X + 1) * tileSize,
    (Y + 1) * tileSize

  return L.latLngBounds(
    L.CRS.EPSG3857.pointToLatLng(topLeftLayerPoint, scale),
    L.CRS.EPSG3857.pointToLatLng(bottomRightLayerPoint, scale)

const tilesBounds = tileCoords.forEach( coord => {
  const singleTileBounds = tileCoordToBounds(coord) 

I recognize that the bounds of tiles contained within fullExtentBounds will reach outside of fullExtentBounds. I'll end up with a larger bounds than I started, and that is fine. Here's a visualization:

enter image description here

I want to make sure that these bounds produce a series of images that is continuous, meaning there are no gaps between tiles, and no overlaps, down to the exact pixel of the original raster source. Meaning that I could lay these tiles out like in the diagram, and they would produce exactly the same image as fetchImageFromBounds(fullExtentBounds).

Does tileCoordToBounds look right, in that the array of bounds are continuous, meaning without gaps and without overlaps?

  • Tiles are numbered from top left to right and then down, without overlapping or having holes (see maptiler.com/google-maps-coordinates-tile-bounds-projection). It's not clear what are your worries about? Tiles themselves overlapping each other or not touching each other or xyz returning overlapping tiles or missing tiles?
    – TomazicM
    Mar 17, 2021 at 9:34
  • Sorry I wasn't clear, I edited my question. Let me know if my concern is clearer now. Mar 17, 2021 at 14:47
  • 2
  • Damn dude, I'm over here reinventing the wheel and its already built in to leaflet. Thank you! I never would have thunk. I can't try this out just yet, but I'm a little concerned about the this used in that function, as that seems to have caused problems for me. I will indeed try it. Still, would you say my function accomplishes the same thing and gives the correct, touching, non-overlapping bounds of the tiles? Mar 17, 2021 at 16:38
  • I seem to be getting an "L.GridLayer._tileCoordsToBounds is not a function" when I try to use that method directly...any ideas? Mar 18, 2021 at 5:28

1 Answer 1


I happen to have just ported to js the function for getting the lat, lng coordinates of a tile.

function get_lat_lng_for_number(xtile, ytile, zoom) {
    let n = Math.pow(2.0, zoom);
    let lng = xtile / n * 360.0 - 180.0;
    let lat_rad = Math.atan(Math.sinh(Math.PI * (1 - 2.0 * ytile / n)));
    let lat = 180.0 * lat_rad / Math.PI;
    return {lat, lng};

You will get the coordinates of the northwestern corner of the tile.

For the southern corners add 1 to y, and for the eastern ones, add 1 to x. For the center, add 0.5 to each.

Complete information may be found on the openstreetmap wiki

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.