Wednesday, March 8, 2017

Moyhu WebGL interactive graphics facility, V2.

As mentioned in the previous post, I've been working on a new version of a WebGL graphics facility that I first posted three years ago. Then it was described as a simplified access to WebGL plotting of data on a sphere, using the active and trackball facilities. It could work from a fairly simple user-supplied data file. I followed up with an even simpler grid-based version, which included a text box where you could just paste in the lat/lon grid data values and it would show them on an active sphere.

So now there is an upgrade, which I'll call V2. Again, it consists of just three files; an HTML stub MoyGLV2.html, a functional JavaScript file called MoyGLV2.js, and a user file, with a user name. The names and locations of the JS files are declared in the html. Aside from that, users just amend the user file, which consists of a set of data statements in Javascript. JS syntax is very like C, but the syntax needed here is pretty universal. The user files must be set before the MoyGLV2.js or equivalent in the HTML.

The main new features are:
  • The merging of the old grid input via a new GRID type, which only requires entry of the actual data.
  • An extension of the user input system that came with the grid facility. A variety of items can now be put in via text box (which has a 16000 char limit).
  • A multi-data capability. Each property entered can now be an array. radio buttons appear so that the different instances can be selected. This is very useful for making comparisons.
  • A flat picture capability. The motivation was to show spheres in 3D, but the infrastructure is useful for a lat/lon projection as well.
  • A compact notation for palettes, with color ramps.

I'll set out the data requirements below the jump, and some information on the controls (which haven't changed much. Finally I'll give a grid example, with result, and also below that the code for palette demo from the last post. The zip-file which contains code and example data is here. There is only about 500 lines of JS, but I've included sample data.

Structure of user file

The user file has extension .js and is linked from the MoyGLV2.html file. You can change the name of the Moy file - in fact, you may well just embed the contents in another html. You can change the name of the user file, altering the html link accordingly. The user file specifies a function PxDat(p,U). p is an object of objects, and U an object containing numbers. You'll probably want to start by adapting the HTML file to point to your file location.

The file starts by defining new user objects, eg
Then you assign attributes, eg
This is all Javascript (like C); strings are quoted, and arrays (which can be nested) in []. The JS use of array is very liberal; you can mix strings, other arrays, objects etc (but not usefully here). The file ends the function with a }. The object definitions must come before properties, but after that order isn't important.

Each object you define must be assigned a type. The basic WebGL types are in U, and are:

I have added special types U.GRID and U.MAP (see below)

Basic data for shading

There are three major data items that need to be supplied for a shaded plot:
  • .nodes. This is a list of points, specified either by lat/lon degrees, or 3D cartesian. lat/lon will be mapped onto a unit sphere. The node list is a flat array, as in [x,y,z, x,y,z,...] or [lat,lon,lat,lon,...] (the system works out which you mean).
  • .links These are pointers into the list of nodes (by node, not by coord). How these are organised depends on type. POINTS don't need links. LINE types are, as you'd expect, linear, and if you don't supply .links, the system will just join the nodes in the sequence presented. Otherwise in the .links order. TRIANGLES expects a list of triples, again as a flat list. The other triangle types are more complicated. Remember that numbering, as in C, starts from zero.
  • .val values, one for each node (but see below for multiples). These are converted to pointers into the color palette by a function which I'll explain below, but which is non-linear, so the ends of the palette represent +-Inf.
The original version expected color pointers (.col) as integer pointers into palette, and you can still use this instead of .val if you wish, using your own mapping. Of course, .cols is also what you need for general drawing.

Setting .type=U.GRID simplifies data input for data from a regular lat/lon grid. .nodes and .links are not required, and will be worked out from the number of .val items (for any reasonable cell rectangle size down to 1/4°).

Colors and presentation.

I said quite a lot about palettes in my previous post. Colors are described in WebGL as rgb on a 0 to 1 range, so [1,0,0] is red and [0,0.999,1] is cyan. You can give a palette by just listing the triples in an array. For points and lines, the palette is usually just one color, and there is no need for .cols or .val, though you can have them if you want color variation. But for triangles, you usually want shading, and I use a 256 color palette as conventional, although others are acceptable. There is a new shortened description for palettes which are ramps of color shade. Each triple is followed by a number indicating how many to ramp until the next color. So rainbow goes from red to yellow to green to cyan to blue thus:
.palette=[1,0,0,64, 1,1,0,64, 0,1,0,64, 0,1,1,64, 0,0,1,1]
The last integer is for format; it isn't used. The program is notified of format by the fourth digit being >1, so while unit ramps are allowed, avoid in the first pairing.

If you don't declare a palette, the program will use rainbow for triangles, else grey [.5,.5,.5] for points and lines.

Some miscellaneous appearance properties:
  • .tags can be useful. You can attach a string to each node in a point object, so its format is an array of strings ["..","..",...]. The effect is that if you click on the plot, information, including name and value, will appear above the color key on the right. If given, there should be one entry per node.
    • .size gives size of points in pixels
    • .long lets you put in a title for the top left
    • .short makes names for the buttons that will appear if you have multi-value (see below). Note that there is usually only one shaded type, and the headings are taken from that, so this is where you should attach them.
    • .units again attach to the shading object. It is a string that will appear above the color key.


    There is just one for now, which is a .from tag that allows inheritance. If you say p.ObB.from=p.ObA, that means that object ObB will inherit properties from ObA, unless they are explicitly assigned in the user file.


    This is a major addition. If you replace any property by an array of specifications, using an array of arrays if necessary, then the program will sort into multiple instances, and create radio buttons and headings on the right under the Orient button. You can use these to switch from one instance to another, which is very useful for comparisons. I'd generally recommend doing this for only one property, or if more, make sure there are the same number of each. You can only usefully do it for one object.

    Flat plots

    The motive for developing this was spherical plots, but the extra facilities are now useful for 2D lat/lon plots as well. There is a parameter U.flat which controls this. Default is 0, but you can set U.flat=1 in the user file, and a 2D plot will result. It won't rotate, but you can zoom. There is a problem at the moment that flat plots seem to sometimes have defects with inappropriate coloring. I've had trouble tracking this down.

    User textbox input on the fly

    This was available in the old grid facility, but is much enhanced. The controls are in the table on the far right. You can select one of a few object types, and put in an appropriate value - again demonstrated in the last post. JS syntax for arrays, strings etc needed. If you used it before entering just a comma-ed string, now you need square brackets for array data. If you have multi-value running, the currently selected (by radio button) instance will be replaced. You can use it to add new headings too. Be careful to use the right data type for your selection. At the moment, you can't enter multi-value as textbox data.

    General program properties.

    I have described the program as an Earth viewer, and that is the intended use. But it was originally a general plotting program, and can still be used as such. You should keep to a scale comparable to the unit sphere, else you will have to zoom a lot. There is a wrinkle that the default puts a disc through the origin which ensures that a transparent earth plot, say just a map, won't be confused with info from the other side. To make this go away, set U.disc=0 in your user file.

    The general expectation is that just a few objects will be used. The map, for example, is just one line object, using NaN's (two for lat/lon) to separate islands.

    Each JS file contains just one function, with names like PxInit(). These are visible globally. Another global is the id of the base element in the HTML file, PxBody. Everything else should be local. Each global name has just one call within the MoyGL JS file, so you can change them. You may want to do this if you embed two instances on the same page.

    The program uses absolute positioning relative to the body element. It should reserve enough space that it won't overwrite text below, but in a blog environment it may spread beyond its column width.

    The color mapping uses a function from the whole real range to the whole palette range. This is a function with a shape like tanh or atan, or cumulative normal etc. I currently use a tanh-like function. The idea is to not reserve too many colors for the outliers. I calculate a sd as the spread parameter; the +- 1 sd gets about 60% of the colors.

    Using Controls

    These are as with my other WebGL apps. The globe is a trackball, dragged with left mouse button down. Clicking that button will also bring up data for the nearest station, if you supplied tags. Dragging with right button vertically zooms the plot. If you want to re-orient but not change the disc you are seeing, use the Orient button. The far right table is for data entry, as explained above. Click Apply when you have selected.

    Code examples and results - simple grid type.

    I'll start with the fairly simple grid style. Here is one with data from NCEP/NCAR for the month of March so far, smoothed with 10-order spherical harmonics. It is a 144x73 2.5° grid, and it uses the MAP type, so the HTML file needs to call Map.js. Here is the HTML:
    <div id="PxBody" />
    <script type="text/javascript" src=""> 
    <script type="text/javascript" src=""> 
    <script type="text/javascript" src=""> 
    The names and locations can be changed, provided you give the right URL, which could be a local file name. Now here is the file called grid.js, with the main dataset truncated. The working file is on the zipfile above, and you can also always access the JS from the URL above.
    function PxDat(p,U){
    I have included the U.flat=0 statement to show how that works. The default is 0, so it does nothing unless you change to 1, when you get a flat plot. I have given no palettes, so defaults will be used. You can see the unsmoothed version I usually post here, although being a current month file, it is likely to change in the next few days. The quality isn't great, because it is a relatively coarse grid.

    Code examples palette demo from previous post.

    Here is the user-editable code from the palette case I showed last post. The HTML file is minimal:
    <div id="PxBody" />
    <script type="text/javascript" src=""> 
    <script type="text/javascript" src=""> 

    The palette.js file could be a local file; you can choose any name (eith URL to match it's location). It is not referred to anywhere else. Now the data specification:
    function PxDat(p,U){
    var Stats, Mesh, Map;
    Mesh.units="Deg C"
    Mesh.long=["Rainbow spectrum","Rainbow tinged red","Rainbow tinged blue","Red, yellow white and blue","Red white and blue","Earth: brown to olive","Earthy with blue","Black to white"];
    Mesh.palette=[[1,0,0,64, 1,1,0,64, 0,1,0,64, 0,1,1,64, 0,0,1,64],[1,0,0,96, 1,1,0,80, 0,1,0,48, 0,1,1,32, 0,0,1,64],[1,0,0,32, 1,1,0,48, 0,1,0,80, 0,1,1,96, 0,0,1,64],[1,0,0,64, 1,1,0,64, 1,1,1,64, 0,1,1,64, 0,0,1,64],[1,0,0,128, 1,1,1,128, 0,0,1,1],[0.62,0.32,0.17,128, 0.7,0.7,0.3,128, 0.4,.6,.2,1],[0.62,0.32,0.17,128, 0.7,0.7,0.3,104, 0.4,.6,.2,24, 0,0,1,1],[1,1,1,256, 0,0,0,1]];
    Mesh.nodes= [36.93,6.95,36.83,7.82,...];
    Mesh.links= [1641,1650,1634,...];
    Mesh.val= [-0.0629,-0.9093,-0.4092,...];
    Stats.tags=Stats.tags=[" SKIKDA"," ANNABA"," DAR-EL-BEID",...];

    I've put in a little live Javascript
    concat() is just JS's way of making a copy. The nodes of the Stats (stations) are the same as Mesh, so I don't need to enter them again (but I could). Probably there was no need to copy; JS would just have a pointed to the Mesh data which would probably be fine. There is a map in this one too, but I have included the data in the user file.


    Post a Comment