Hi,
I’ve been bragging about Globus recently – allegedly, you can make your own projections for it. It’s true, let me show you how to do it!
First here are links to some projections, which are available from the drop down list. We’ll use them for reference:
Please open equirectangular. First thing you’ll see is:
...SECTION TOCALCULATIONS:
Projection files are divided into sections. There are 7 sections:
 TOCALCULATIONS – responsible for calculations if you convert TO this projection.
 FROMCALCULATIONS – responsible for calculations if you convert FROM this projection.
 TOFORM – if you convert to this projection, this is the HTML form that appears below Project to:. You will use it to get additional data from user.
 FROMFORM: if you convert from this projection, this is the HTML form that appears below Project from:. You will use it to get additional data about the image you’re working with – what is its central meridian, etc.
 OUTPUTWIDTHCALCULATIONS – javascript code which is responsible for setting width of output image.
 OUTPUTHEIGHTCALCULATIONS – javascript code which is responsible for setting height of output image.
 MERIDIANLENGTHCALCULATIONS – javascript code which will calculate length of meridian in pixels based on width and height of input image.
Now let me tell you what exactly does each section, and how to write them. Let’s project from Equirectangular to Sinusoidal. But beware, I will not go in order!
3. TOFORM:
When we project to Sinusoidal, we want the user to pick the meridian that will be in center. What is the way to collect data from a user on a website? HTML forms. We need to write html code that will appear below Project to:, and will let the user pick the central meridian. Here’s how we did it:
1

Pick central meridian (from 180 to 180): <input max=“180” min=“180” name=“meridian” type=“number” />

Fairly simple form – just one input. You can write any html code you wish, you may even add <script> … </script> sections. Then, once the user presses Project, Globus Map Projector will extract the data from the form, and enable you to use it when calculating the projection. However, only some input types will be extracted by Globus:
 <input type=”number” name=”someFloat”>
 <input type=”color” name=”someColor”>
 <input type=”range” name=”someFloat>
 <input type=”checkbox” name=”someBool”>
 <input type=”radio” name=”someInteger”>
 <select name=”someInteger”>
As you have probably noticed each of those inputs was also given a name. It is crucial – you will use this name as a name of a variable when you calculate the projection. You will learn how to use those variables in the next section.
1. TOCALCULATIONS:
This is the very heart of our projection. Here we define how to project TO our sinusoidal projection. Such calculations are done on shaders in Globus Map Projector, so this section must be written in GLSL (GL Shader Language). Basically, it is very similar to C, however it has some important differences, which I will list at the end of this section.
TOCALCULATIONS’s job is simple. As input you get coordinates of a point in your TO projection. Then you need to return where this point is located in equirectangular projection. Let me show this in a picture:
We get a point on output image, for example point (x, y):
We need to find where this point is in equirectangular projection. When TOCALCULATIONS’s section finishes, color from pixel (x2, y2) will be set in our output image at pixel (x, y).
Let’s go to practice. TOCALCULATIONS section is body (code between the curly brackets) of a function of this type:
1
2 3 4 5 6 7 8 
vec2 projectPointFromSinusoidalToEquirectangular(vec2 coords)
{ // do your projection code float x = coords.x; // coords.x is X coordinate of projected point (check the black and white image few lines earlier) float y = coords.y; // coords.y is Y coordinate of projected point return vec2(x, y); // we return a vector which contains point (x2, y2) from above image. 
As you have probably noticed, this code does nothing – it projects a point to itself. To make a real projection we need to learn three more things.
1. All coordinates (input and returned) are floating point numbers between 0 and 1. x = 0 is the left side, x = 1 is the right side, y = 0 is the top, and y = 1 is the bottom.
2. If we want a given point to be blank (if the point doesn’t belong to the projection), we return vector with at least one negative coordinate. For example: return vec2(1.0, 1.0); // this point is blank
3. We get access to our variables from TOFORM section. We will call the variables that the user created uniforms (because they behave just like GLSL’s uniforms), and they are constants defined at the top of our function. Check this list:
 <input type=”number” name=”someFloat”> will generate:
1const float someFloat = …..; // number provided by the user
 <input type=”color” name=”someColor”> will generate:
1const vec4 someColor = …..; // color provided by the user
 <input type=”range” name=”someFloat”> will generate:
1
2const float someFloat = …..; // selected number from range. For example if the user picked 12/250
// this will be: const float someFloat = 12.0;  <input type=”checkbox” name=”someBool”> will generate:
1const bool someBool = …..; // true if the checkbox was selected, false if it wasn’t selected
 <input type=”radio” name=”someInt”> will generate:
1
2
3
4
5const int someInt = …..; // Globus will count all the radios with the same name and return index
// of the one that was selected. For example, if we have 5 radios with
// name “radio123”, in 5 lines, and the radio in 3rd line was selected
// we’ll get: <em>const int radio123 = 2;</em> and if the radio from 1st line was
// selected we’ll get: <em>const int radio 123 = 0;</em>  <select name=”someInt”> will generate:
1const int someInt = …..; // index of selected option (remember we count indexes starting from 0)
Knowing that we can now fully understand our sinusoidal projection. Analyze it yourself, it should be quite simple now!
Important things to know about GLSL in WebGL:
 When you make any type of operations (conditions, mathematical operations, etc.) on numbers, they need to be of the same type. Say you want to check if 5 < 6.12. It will give you a compiler error, as it should be 5.0 < 6.12. Or you have float number and int i. In this case you need to do it like: number + float(i). Which leads us to:
 Conversion. Unlike in C++, or C you don’t have the typical (int) myFloat construct. You will use constructors – just like this: int(myFloat) or float(myInt).
 In WebGL version of GLSL you cannot use loops. Well, most of the times. While won’t work, same with do…while. For works only if the condition is evaluable during compilation. What it means, is that your for usually has to be of this type: for (int i = 0; i < someConstant; ++i). Lucky for you, all the numbers provided by the user in HTML forms are constant.
4. FROMFORM:
Very similar to TOFORM. However, in this form, the user writes with what parameters they created the FROM projection. In our case, if the FROM projection (equirectangular) was made using 80 as central meridian, the user writes 80 in pick central meridian. Data collected from user in this section will be sent to FROMCALCULATIONS.
2. FROMCALCULATIONS:
Opposite to TOCALCULATIONS – you need to calculate, where a point in equirectangular projection is on your projection. Let me illustrate that.
You have a point (x2, y2) on equirectangular projection:
And then, you want to find its position on your projection (or, in other words, project it to your projection):
FROMCALCULATIONS programming is identical to TOCALCULATIONS. You need to write body (code between the curly brackets) of a function of this type:
1
2 3 4 5 6 7 8 
vec2 projectPointFromEquirectangularToSinusoidal(vec2 coords)
{ // do your projection code float x = coords.x; // coords.x is x2 from pictures above float y = coords.y; // coords.y is y2 from pictures above return vec2(x, y); // we return a vector which contains point (x, y) from above image. 
You will have access to uniforms from FROMFORM just as in TOCALCULATIONS you get access to uniforms from TOFORM.
7. MERIDIANLENGTHCALCULATIONS:
This section’s job is to say how long (in pixels) a meridian is in our FROM projection. In our case, the input image is in equirectangular (we project from equirectangular), so meridans span from the very top of the image to the very bottom:
That’s why the length of meridian in pixels is input image height. However, we also know that in equirectangular projections height = width/2, so meridian = width/2. Of course we could use meridian = height just as well.
Ok, so now how to write it?
MERIDIANLENGTHCALCULATIONS section is javascript code. Imagine it is a function (it actualy is a function, run through eval()) which gets width and height of the input image, and uniforms (from Project from form). Your job is to write body of a function (code between curly brackets) that will return the length of meridian.
1
2 3 4 5 
function calculateMeridianLength(width, height, uniforms)
{ // do some code return …..; // return length of meridian } 
Few lines ago I mentioned that MERIDIANLENGTHCALCULATIONS also receives the uniforms from FROMFORM. Why? Check sinusoidal intercepted projection – it turns out, that we can make the gores either parallel or concentric. In each of those cases projection size is different: in parallels width/height = 2, but in concentric width = height. This means that we have to distinguish type of gores when we calculate length of meridian. That’s when uniforms come in handy. Uniforms is an array of uniforms, and each uniform is a structure containing 3 fields:
 type (float, int, bool, vec4)
 name (taken directly from HTML form)
 value (value given by the user)
If we want to get a uniform of a given name from this array, we can use getUniformFromArray(array, name) method. It’s quite easy, let’s check it on sinusoidal intercepted projection:
1
2 3 4 5 6 7 8 9 
switch (getUniformFromArray(uniforms, ‘interceptionType’).value)
{ case 0: return width/2; break; case 1: return width; break; } 
Let’s concentrate on:
getUniformFromArray(uniforms, 'interceptionType').value
As you can see, we simply ask to extract uniform of name interceptionType, and once we get it, we extract its value. Quite simple, isn’t it?
5, 6. OUTPUTWIDTHCALCULATIONS and OUTPUTHEIGHTCALCULATIONS:
This javascript sections are used to set the size of output image. The input is length of meridian in pixels and uniforms from TOFORM; as the output, you need to return either width or height.
I hope that now it’s clear how to make your own projections. I encourage you to write some, and you can send them to winski@winski.net, so I can check them, and then add them to the website. Don’t forget to write your name (in the code or in the forms) if you want attribution, and give permission to use it or add license notice to the code.
Write in comments, if you have questions!
Thank you and cheers 🙂