1

I am new to GIS in general and struggling with the correct approach to achieve my goal.

I have a list of 100,000 or so Lat/Lng taken from a GPS and I need to retrieve the singular tile for each of these coordinates from a (hosted) map in the EPSG:27700 projection. Ideally I would use python for this as I do not need to visualise the data and I need to perform some histograms on the respective tile/latLng pair.

The following JavaScript code is used to configure leaflet to display the entire map (using proj4j to set the custom espg27700):

var r = [5734.4,2867.2,1433.6,716.8,358.4,179.2,89.6,44.8,22.4,11.2,5.6,2.8];

var BNG = new L.Proj.CRS("EPSG:27700","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs",  
  bounds: L.bounds([[0, 0], [1468006.4, 1468006.4]])
  resolutions: [5734.4,2867.2,1433.6,716.8,358.4,179.2,89.6,44.8,22.4,11.2,5.6,2.8]},);

The tileset is TMS

Broadly speaking, I am not sure what steps I need to take a given lat/lng and zoom level and get the correct z/x/{-y}

Following similar links here suggests I need to convert to projected coordinates first, which gives me coordinates similar to [622673.7010631626, -5527546.628687883].

This is where I am not clear on what I need to do next to convert those for any given zoom level?

I tried the following suggestion (I lost the original link)

  function convertLt(lat,lng, z) {

      var origin = [ -238375.0, 1376256.0 ]
      
      var source = new proj4.Proj('EPSG:3857');

      proj4.defs("EPSG:27700","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs");

      var dest = new proj4.Proj('EPSG:27700');

      var coords=proj4.transform(source, dest, [lat,lng]);
      console.log(coords)

      var resolutions = [5734.4,2867.2,1433.6,716.8,358.4,179.2,89.6,44.8,22.4,11.2,5.6,2.8];
      
      x = Math.floor((coords[0] - origin[0]) / (resolutions[z] * 256));
      y = Math.floor((origin[1] - coords[1]) / (resolutions[z] * 256));
      console.log(x)
      console.log(y)
      return (x,y)
  }

The above code doesn't work for me, returns NaN. I'm not sure what values should be placed in the origin.

To clarify, I would like my input to be Lat/Lng and output to be z/x/y (considering TMS, so inverted y) so I can fetch the single tile I require.

1
  • Welcome to Geographic Information Systems! Welcome to GIS SE! We're a little different from other sites; this isn't a discussion forum but a Q&A site. Your questions should as much as possible describe not just what you want to do, but precisely what you have tried and where you are stuck trying that. Please check out our short tour for more about how the site works
    – Ian Turton
    Commented Jan 25, 2023 at 12:23

2 Answers 2

1

One possible way to get tile (x, y, z) coordinates for specific [lat, lng] coordinate and specific projection is to 'misuse' generic Leaflet L.TileLayer layer and 'tileload' layer event.

Procedure goes as follows:

  • Create additional auxiliary map with desired projection in it's own div element, which is not displayed at all and has dimension 1 x 1 px.
  • Create custom layer by extending L.TileLayer just to fire 'tileload' event and add it to the aux map,
  • Create function for tile coordinates retrieval, which is based on catching 'tileload' event after centering auxiliary map view to desired coordinate and zoom.

This description probably sounds a bit abstract, but code is actually quite simple.

CSS for auxiliary map:

#gridmap {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  visibility: hidden;
}

HTML for auxiliary map:

<div id="gridmap"></div>

JS for auxiliary map:

var gridmap = L.map('gridmap', {
  crs: BNG
});

L.TileLayer.Grid = L.TileLayer.extend({
  createTile: function (coords) {
    var tile = document.createElement('div');
    return tile;
  }
});

L.tileLayer.grid = function(opts) {
  return new L.TileLayer.Grid(opts);
};
var gridLayer =  L.tileLayer.grid({
  tms: true
});

function getTileForCoordAndZoom(coord, zoom, callback) {
  gridLayer.on('tileload', function(evt) {
    gridLayer.off('tileload');
    callback(evt.coords);
  }); 
  gridmap.setView(coord, zoom);
}

Tile coordinate for specific coordinate and zoom can then be retrieved like this:

getTileForCoordAndZoom([52.093992, -1.5], 8, function(tileCoord) {
  console.log(tileCoord);
});
1
  • Thanks for the suggestion. This got me close enough to figure out I was overcomplicating things somewhat. Will post an answer with the solution I used
    – orianem
    Commented Jan 30, 2023 at 11:54
0

It seems I was overcomplicating this somewhat and I can leverage leaflet's project function to convert lat/lng to pixel coordinates. It is then a case of dividing returned values by tile size to get the tile server X and Y for a given Zoom level.

Use the following with leaflet

var BNG = new L.Proj.CRS("EPSG:27700","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs",  
  bounds: L.bounds([[0, 0], [1468006.4, 1468006.4]])
  resolutions: [...]
}

//...

const map = L.map('map', {crs:BNG,}).setView([51.505, -0.09], 4);

worldCoords = map.project([51.49386, -0.068333], 10).floor();

tileX = worldCoords.x / 256; //assuming 256px tiles
tileY = worldCoords.y / 256;


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.