🏠 back to Observable


A big milestone for this work-in-progress that’s been going on for like 3 or 4 years now! It’s an implementation of the Goode Homolosine projection in GLSL, generic enough to cover the “interrupted” case (up to 4 lobes per hemisphere).

The technique was pioneered by Mike in his “Milky Way” block[1], which I’ve spent literally hundreds of hours studying. It’s using the GPU to project a raster image, which implies calling an inverse projection: for each pixel on the screen, the GPU computes the corresponding coordinates on the sphere, then checks the color of those coordinates in a raster image, uploaded as a texture.

I extended the projection system so it can be kept in sync with (most of) the projection’s attributes: rotate (with 3 Euler angles), translate, scale, center, angle. All this makes it possible to work with animation and interaction. (In this case, a simple translate+scale zoom.)

The first difficulty is that you need to write that inverse projection in GLSL, because it’s going to be run on the GPU. Some of the projections can be ported from d3-geo’s source code, but it’s sometimes quite difficult and always a manual process. (I now have draft implementations for about 20 of them: there’s still a lot to do if we want to cover the whole set of projections available in D3!)

The second difficulty is that running WebGL is hard. In this notebook, I’m using regl to control the state of the GPU, which limits the quantity of “boilerplate” code that needs to be written, and takes care of the tedious work of uploading the picture, converting values to uniforms etc.

Still, it’s a complicated beast and there are lots of cases where you just don’t understand what’s going on. Some challenges, for example:

  • making the poles look approximately OK (the equirectangular textures are completely distorted at the poles)
  • making the antimeridian line look perfect (the problem is not with the texture, which can be wrapped, but with the mipmap interpolation that kicks in when going from 0.0 to 1.0).
  • making animation smooth (by default it tends to scintillate).
  • downloading an low resolution image first, then a higher resolution.
  • respecting each browser’s idiosyncrasies and client’s capabilities (texture size is limited, etc).
  • reuse the produced raster projection in a usual canvas, so it can be mixed with D3 geoPath

I hope the code is readable enough, please don’t hesitate to ask about details—I want to do a proper write-up at some point and will use your questions to orient and expand it.

(Many thanks to Mike who initiated this with Jason, to Maarten who pushed me to finish, and Ricky who helped me fine-tune the regl options.)

[1] “Milky Way” http://bl.ocks.org/mbostock/5446416