Leaflet WebGL many points rendering

WebGL is funny – programming in very low level style in JavaScript. This sample plots 86T points using this technology.  .

 

 

 

The code is very straightforward, the only thing is to how points are initially loaded and scaled (instead of reloading each time when map moves).

All points are initially transformed to tile size of 256 x 256 pixels at zoom level 0  and then re-scaled/re-shifted based on the current position of the map. drawingOnCanvas is called from L.CanvasOverlay each time map needs to be drawn (move, zoom)

 


function drawingOnCanvas(canvasOverlay, params) {
  gl.clear(gl.COLOR_BUFFER_BIT);
  // -- set base matrix to translate canvas pixel coordinates -> webgl coordinates
 mapMatrix.set(pixelsToWebGLMatrix);
  var bounds = leafletMap.getBounds();
  var topLeft = new L.LatLng(bounds.getNorth(), bounds.getWest());
  var offset = LatLongToPixelXY(topLeft.lat, topLeft.lng);
  // -- Scale to current zoom
  var scale = Math.pow(2, leafletMap.getZoom());
 scaleMatrix(mapMatrix, scale, scale);
 translateMatrix(mapMatrix, -offset.x, -offset.y);
  // -- attach matrix value to 'mapMatrix' uniform in shader
  gl.uniformMatrix4fv(u_matLoc, false, mapMatrix);
 gl.drawArrays(gl.POINTS, 0, numPoints);
}

More information and insipiration I took from this site

demo here: http://bl.ocks.org/sumbera/c6fed35c377a46ff74c3

For polygons rendering check here and for polyline rendering here

Some good intros to WebGL that might help you to understand the code: http://aerotwist.com/presentations/custom-filters/#/6

There is a nice intro book to WebGL  WebGL Programming Guide by Kouchi Matsuda and Rodger Lea

To illustrate how variables are passed from JavaScript to shaders used in above example, here are two figures from the book-  figure 5.7 on p. 149, and figure 5.3 on p.144.

strideoffset

Stride and Offset

This figure shows single buffer (interleaved)that is used fro both coordinates and size. In similar way single buffer is constructed in the example here:

 

 

 

var vertBuffer = gl.createBuffer();
var vertArray = new Float32Array(verts);
var fsize = vertArray.BYTES_PER_ELEMENT;

gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertArray, gl.STATIC_DRAW);
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, false,fsize*5,0);
gl.enableVertexAttribArray(vertLoc);
// -- offset for color buffer
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, fsize*5, fsize*2);
gl.enableVertexAttribArray(colorLoc);

 

shadervariables2

behavior of a varying variable

12 thoughts on “Leaflet WebGL many points rendering

  1. moovida

    Thanks for this, it is really a great tutorial!
    I am trying it out with very narrow points and it doesn’t work as expected, I assume because the calculations are done on the zoom 0 and there are rounding issues (the points appear aligned on a evenly spaced grid).
    I wasn’t able to understand if there is a way to handle this. Is there a way to make the LatLongToPixelXY method work for the current zoomlevel?
    Thanks

    Reply
    1. Stanislav Post author

      this is most likely rounding issue,
      here is a sample of calculating initial point position at zoom 11, called _groundZoom, from where _groundWorldSize is calculated and passed to GLMath.* function to get pixels coordinates in that zoom level.

      var _groundZoom = 11; // — reference zoom level where pixel coordinates are calculated
      var _tileSize = 256;
      var _groundZoomScale = Math.pow(2, _groundZoom);
      var _groundWorldSize = _tileSize * _groundZoomScale;

      //– from transform.js in mapbox-gl-js lat/lon absolute pixel coords convertion
      GLMapMath.lngX = function(lon, worldSize) {
      return (180 + lon) * (worldSize || this.worldSize) / 360;
      };
      //– from transform.js in mapbox-gl-js latitude to absolute y coord
      GLMapMath.latY = function(lat, worldSize) {
      var y = 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360));
      return (180 – y) * (worldSize || this.worldSize) / 360;
      },

      // — to get point for ‘groundWorldSize…
      point = { x: GLMapMath.lngX(data.geometry.coordinates[0], _groundWorldSize), y: GLMapMath.latY(data.geometry.coordinates[1], _groundWorldSize) };

      Reply
      1. moovida

        Hi, thanks for your reply. I am trying to do the proposed change. Since I couldn’t find a reference to GLMapMath I just took the methods you supplied. The result are huge x,y values outside the range. am I right that this needs to be applied in LatLongToPixelXY with the proper zoomlevel?

  2. Stanislav Post author

    above functions will give you X,Y in pixel size at defined zoomlevel which gives very large number with deeper zooms. It might be problem (but I havn’t faced it so the problem might lie somewhere else).
    it is hard to say where problem is without facing it, any sample /gist to showcase this ?

    Reply
    1. moovida

      Hmmm, I think I understand what you mean, but thought the offset would take care of that. I went crazy trying (you will find many LatLongToPixelXY* methods :-) ). I uploaded my last tries in this share db folder. I didn’t create the gist because the json should be at least a bit datarich. I hope that is ok: https://www.dropbox.com/sh/z97iia6gldsgox4/AACWWxqmP0D7BKXa2F2eJ2yWa?dl=0

      The dataset is a small zoom of a laserscan dataset, and you should be able to see trees on the terrain in greyscale.
      As it is now, it should open up and visualize the dots as per your code and zooming in will produce the grid. I also tried to visualize squares instead of dots but also failed due to ignorance in webgl, so you will find some changes due to that. Thanks

      Reply
  3. pezhsp

    Hello! I’ve also the grid problem on near Points. How can i usw your function with the zoom level. My pixel coordinates also huge and outside of the range. What can I do?

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.