Map projection converter

2024
Python
GLSL

A blazing fast, shader based image projection converter. Converts between the equirectangular and mercator projections. Can be used via a CLI or directly in Python. Check it out on on GitHub!

fragment_to_equirectangular.glsl
vec2 equi_to_merc(float u, float v)
{
    // uv to equirectangular
    float lat = remap(u, 0, 1, 0, 2 * M_PI);
    float lon = remap(v, 0, 1, -M_PI * 0.5, M_PI * 0.5);

    // equirectangular to mercator
    float x = lat;
    float y = log(tan(M_PI / 4 + lon / 2));

    // mercator to uv
    x = remap(x, 0, 2 * M_PI, 0, 1);
    y = remap(y, -M_PI, M_PI, 0, 1);

    return vec2(x, y);
}

Using the moderngl python library, the converter runs the input image through a remapping shader. The shaders transform uv coordinates to latitude and longitude and then convert between the two projections.

Additionally, this implementation can also render with either nearest or linear texture sampling, letting you choose between a sharp or interpolated upscaling.

cpu_render.py
def equi_to_merc(u, v):
  # uv to equirectangular
  lat = remap(u, 0, 1, 0, 2 * math.pi)
  lon = remap(v, 0, 1, -math.pi * 0.5, math.pi * 0.5)

  # equirectangular to mercator
  x = lat
  lon = clamp(lon, -MERC_MAX_LON, MERC_MAX_LON)
  y = math.log(math.tan(math.pi / 4 + lon / 2))

  # mercator to uv
  x = remap(x, 0, 2 * math.pi, 0, 1)
  y = remap(y, -math.pi, math.pi, 0, 1)

  # clamp
  x = clamp(x, 0, 1)
  y = clamp(y, 0, 1)

  return (x, y)

A CPU implementation using the Pillow library exists as a fallback, although with obvious downsides of poor performance and scalability.

Back to projects