Spinning globe


3 Mar 2013

Introduction

This post describes my highly convoluted method of making an animated globe using Khan Academy's computer science framework.

A few weeks ago I found a set of coordinates for a world map in JSON format. I can't remember where exactly, but I think it was via a StackOverflow question. The first thing I did was to convert it into an SVG world map, because I like SVG and I've been meaning to get a world map for a while.

Then, since I've been using Khan Academy's computer science section a lot recently, I made a KA version using Processing. In both cases, I scaled the data to useful dimensions and then rounded to a sensible precision; since KA's canvas only accepts integers, there was no point have coordinates to eight decimal places. I then striped out any points that were identical to preceding points after rounding to reduce redundancy. I could have reduced redundancy further by removing multiple points lying on a straight line, but it turned out to be trickier than I expected to do well.

And then, I started wondering how I could draw an image of a globe using this information.

Diversion into Pygame

My first idea was to draw the image of world map off screen and use get(x, y) to find the colour of pixels on the off-screen map, but that didn't seem to work and I thought might be a bit slow anyway. So, I converted the data into yet another format: pygame.

So, I wrote a Python program that would write a Pygame program that would draw the world on its screen as a series of black polygons on a white background. Then it looped through all the pixels on the screen and recorded whether they were white or black. It then outputted this information to a text file as a series of 0s and 1s, in which each line represented a row of the screen and each character represented a pixel of the screen. The text file actually looked like a world map when I zoomed out.

world_map_binary_crop.png

The point of all this was to get 2D grid that could be used to determine whether there should be land or sea. The alternative would have been to try and work it out myself based on the coordinates for the outlines of the landmasses, but I don't know how to do that (though it would be interesting to work it out). I chose to save the data (in a variable called land) as an array of strings, which is more compact than an array of arrays and simpler than an array of binary or some other type of number.

I could then find out if there should be land at coordinate (x, y) with:

land[y].charAt(x) === "1";

Projections

The problem then was to convert from a 2D rectangular array into a 2D spherical surface in three dimension and to the 2D circular projection of that surface onto the screen. I don't know much about the various ways to project the surface of a sphere onto two (flat) dimensions, other than there are a lot of them.

My naïve approach was to draw a filled circle of pixels using information from a square of the landmass data. I assumed that the y-value, or latitude, would correspond directly and so it was just a matter of working out how the x-coordinate mapped from the circle to the square. As it happens, I think the map is an equirectangular projection, so my approach turns out to be correct.

dx dy θ

For a given $y$ value on the circle, I could calculate the width of the circle ($2 \cdot dx$ above) with a bit of trigonometry as:

$width = r \cdot sin(\theta)\text{, where } \theta = arccos\left(\frac{y}{r}\right)$

This gets the maximum width of the circle at that latitude, so to map that to the maximum extent of the square, you just divide the radius (also the width of the square) by that value. This allows you to scale an x value on the circle to an x value on the square.

Then it was just as a matter of changing where the square started on the map (and wrapping values appropriately) to create the spinning effect and rotating the globe by 23 degrees to get it in the plane of the sun. It might be nice to shade the globe so it looks like it is light from the side, which shouldn't be too hard as I'm already colouring each pixel individually.

Comments (4)

Anonymous on 28 May 2013, 2:33 p.m.

Great article, but I'm struggling with mapping a square image to a circle in pygame because colouring each pixel individually is too slow. How on earth did you pull this off, programming-wise? The math is sound, kudos on the math--it's the Python implementation that boggles me.

mark ptak on 2 Jul 2013, 2:53 p.m.

Glad I found your page. Thanks for all of the incredible work out at Khan Academy!

Ashok Koparday on 15 Jul 2013, 3:01 p.m.

Wow!

Eric Balingit on 6 Sep 2013, 9:05 p.m.

@Anonymous or for anyone else who's interested... I found this globe mapper which is nice. It can be used to convert any projection map into any other projection.

http://www.giss.nasa.gov/tools/gprojector/

For example, I used it to convert a mercator projection into a cube projection which can then be used with mtrn's projection algorithm - which is apparently very fast, but unfortunately I can't help with python conversion. You may have to arrange/flip etc. the cube faces to get the cube map into the correct orientation.

https://www.khanacademy.org/cs/ts/1759209148

Btw, Peter, I like your moon map. I'm still looking for a decent sky map (good 360 resolution) that I can use, but when I find one, I'd like to create a "You are here" Earth program, similar to your Earth and Moon programs, with some nice satellite based maps and correctly oriented constellations with day/night, city lights, etc.

Nice work on KACS!