The JS9 Public API

The JS9 Public API provides a JavaScript programming interface for interacting with JS9. Obviously, all of JS9 JavaScript code is available to you within a web page, but the public API is designed to stable and well-documented for users and web designers. It also will provide the basis for planned language bindings (python, perhaps C/Fortran).

In general, the public API routines act on the current image being displayed in the default JS9 div element. Since most web pages will have only a single JS9 div element, this behavior is usually what you want. It parallels DS9's behavior in which XPA commands act on the currently displayed image. For example, to change the colormap of the current image, use:

    JS9.SetColormap("cool");

However, for cases where multiple JS9 div elements are defined on a single page, you can specify the specific div element to process by adding an display object argument to the calling sequence with a single display property:

    {display: [div_id]}
where [div_id] is the id of the target JS9 div element. For example, if two JS9 divs with ids "JS9" and "myJS9", respectively, are present on a single web page, you can set the colormap of the second one this way:
    JS9.SetColormap("cool", {display: "myJS9"})
Note that this display object contains only the display property and always is specified as the final argument in a call.

The display property can specify an image handle instead of a JS9 div id. When the image handle in question points to the currently displayed image, this alternate usage is nothing more than a trivial optimization. Thus, to call a routine such as JS9.WCSToPix() or JS9.PixtoWCS():

    var im = JS9.GetImage({display: "myJS9"});
    for(i=0; i<1000; i++){
      x = ...
      y = ... 
      wcs = JS9.PixtoWCS(x, y, {display: im});
      console.log("%s %s -> %s %s", x, y, wcs.ra, wcs.dec);
    }
However, the image handle does not need to point to the currently displayed image. When it does not, the public routine will act on the image associated with the image handle, not the current image. In particular, you can use JS9.LookupImage() to get the image handle of any image, and then act on that image. For example, if two images, foo1.fits and foo2.fits, are loaded into a JS9 display, and foo1.fits is currently being displayed, you can close foo2.fits this way:
    var im = JS9.LookupImage("foo2.fits");
    if( im ){
        JS9.CloseImage({display: im});
    }

The optional display object argument generally is not mentioned in the routines below, but it is always available.

Choose from the following sections:


Loading Images

Load an image into JS9

JS9.Load(url, opts)

where:

Load a FITS file or a PNG representation file into JS9. You also can pass an in-memory buffer containing a FITS file, or a string containing a base64-encoded FITS file.

Finally, you can pass a fits object containing the following properties:

The difference between JS9.RefreshImage() and JS9.Load() is that the former updates the data into an existing image, while the latter adds a completely new image to the display.

To override default image parameters, pass the image opts argument:

    JS9.Load("png/m13.png", {scale:"linear", colormap:"sls"});

If an onload callback function is specified in opts, it will be called after the image is loaded:

    JS9.Load("fits/3c273.fits", {scale: "linear", onload: func});
The image handle is passed as the first argument to the callback.

To load an image into a specified display, pass the display object as the last argument:

    JS9.Load("fits/3c273.fits", {scale: "linear"}, {display: "myJS9"});

See Displaying Your Data for further discussion of how to use this routine.

Load an image into a light window or a new (separate) window

JS9.LoadWindow(url, opts, type, html, winopts)

where:

returns: This routine will load an image into a light-weight window or an entirely new window. The url and opts arguments are identical to the standard JS9.Load() call, except that opts can contain an id string to specify the id of the JS9 div being created. If no id is specified, a unique id is generated. In either case, the id is returned by the call.

The type argument determines whether to create a light-weight window or a new (separate) window.

By default, the new window will contain a Menubar above an image display area:

    <div class='JS9Menubar' id='[id]Menubar'></div>
    <div class='JS9' id='[id]'></div>
You can use the html argument to supply different web page elements for the window. Furthermore, if you create a light window, a default set of DynamicDrive dhtmlwindow parameters will be used to make the window the correct size for the default html:
    "width=512px,height=542px,center=1,resize=1,scrolling=1"
You can supply your own parameters for the new dhtmlwindow using the winOpts argument. See: DynamicDrive for more information about their light-weight window.

To create a new light window without loading an image, use:

    JS9.LoadWindow(null, null, "light");

See js9create.html for examples of how to use this routine.

Load an image URL into JS9 using a proxy server

JS9.LoadProxy(url, opts)

where:

Load a FITS, PNG, or JPEG file specified by an arbitrary URL into JS9 using the JS9 back-end helper as a proxy server.

For security reasons, JavaScript contained in one web page can access data in another web page only if both web pages have the same origin (i.e., basically coming from the same host). This policy is called the Same Origin Policy. This means that JS9 cannot load a image files (i.e., FITS, PNG, or JPG) from an arbitrary URL without using special techniques.

One such technique is Cross-Origin Resource Sharing, by which the second server grants permission to access its image data. Dropbox is CORS-enabled, so that image files can be loaded directly into JS9. But obviously this requires that explicit permission be granted by the other server.

A second technique is to use a proxy server: the URL is not loaded directly into JS9, but instead is copied back to the server from which JS9 itself was loaded. The file is then retrieved by JS9 from this server so that the "same origin" rules are not violated.

The JS9 Node.js back-end helper can be configured to support proxy server mode by setting the JS9.globalOpts.loadProxy property. In addition, the back-end server must be configured to support temporary working directories for each loaded page by setting the JS9.globalOpts.workDir property. If the back-end server is thus configured as a proxy server, JS9 will support the JS9.LoadProxy() call and allow you to load FITS, PNG, and JPG files from arbitrary URLs. JS9 also will display a open link via proxy menu option in the File menu.

The JS9.LoadProxy() call takes a URL as its first argument. This URL will be retrieved using curl or wget and stored on the back-end server in a directory specifically tied to the web page. (The directory and its contents will be deleted when the page is unloaded.) JS9 then will load the file from this directory. Note that since the file resides on the back-end server, all back-end analysis defined on that server is available.

To override default image parameters, pass the image opts argument:

    JS9.LoadProxy("http://hea-www.cfa.harvard.edu/~eric/coma.fits", {scale:"linear", colormap:"sls"});'

If an onload callback function is specified in opts, it will be called after the image is loaded:

    JS9.LoadProxy("http://hea-www.cfa.harvard.edu/~eric/coma.fits", {scale: "linear", onload: func});'
The image handle is passed as the first argument to the callback.

To load an image into a specified display, pass the display object as the last argument:

    JS9.LoadProxy("http://hea-www.cfa.harvard.edu/~eric/coma.fits", {scale: "linear"}, {display: "myJS9"});'

Note again that not all back-end servers support the proxy functionality. The main JS9 web site does support proxy service, and can be used to view images from arbitrary URLs.

Load one or more images when the web page is ready

JS9.Preload(url1, opts1, url2, opts2, ... url2, optsn)

where:

This routine will pre-load images into a JS9 display. It can be added to the web page body element using the onload() JavaScript call or called in a JavaScript init routine tied to onload. See index.html and js9preload.html for examples.

It is worth emphasizing that JS9.Preload() should not be called until the web page is fully loaded, since JS9 itself is not fully initialized until then. It is for this reason that JS9.Preload() generally is called using an onload routine tied to the web page body.

Get Load Status

status = JS9.GetLoadStatus(id)

where:

returns: This routine returns the status of the load process for this image. A status of "complete" means that the image is fully loaded. Other statuses include: Thus, to check for image load, one can do this in python:
    tfits = "foo.fits"
    hdul = fits.open(tfits)
    ...
    j = JS9()
    j.SetFITS(hdul, tfits)
    while j.GetLoadStatus(tfits) != "complete":
        time.sleep(0.1)
    j.SetZoom(2)


Working with Images

Get image handle for the current image

im = JS9.GetImage()

returns:

The routine returns the image handle associated with the current image.

The returned image handle can be passed in the display object. This is marginally more efficient than the default behavior, which is to determine the current image for each call.

Lookup an image by id

im = JS9.LookupImage(id)

where:

returns:

The JS9.LookupImage() routine takes a string id as input and returns the image handle of the image having that id (or null). The id is the same as is found in the File menu list of displayed images. This routine is similar to the standard JS9.GetImage() routine, but returns an image by name, regardless of whether it is currently being displayed.

You can use this routine, for example, so close an image that is not currently being displayed. If two images, foo1.fits and foo2.fits, are loaded into a JS9 display, and foo1.fits is currently being displayed, you can close foo2.fits this way:

    var im = JS9.LookupImage("foo2.fits");
    if( im ){
        JS9.CloseImage({display: im});
    }

Get image data and auxiliary info for the specified image

imdata = JS9.GetImageData(dflag)

where:

returns:

The image data object contains the following information:

This call can return raw data for subsequent use in local analysis tasks. The format of the returned data depends on the exact value of dflag. If dflag is the boolean value true, an HTML5 typed array is returned. In JavaScript, typed arrays are more efficient than ordinary JavaScript arrays, and, in this case, the returned data is actually just reference to the real JS9 image data (so be careful about changing values).

If dflag is the string "array", a JavaScript array is returned. This is not a reference to the real data and will utilize additional memory, but the values can be manipulated safely.

If dflag is the string "base64", a base64-encoded string is returned. Oddly, this seems to be the fastest method of transferring data to via socket.io an external process such as Python, and, in fact, is the method used by the pyjs9.py interface. (The "array" method also can be used, but seems to be slower.)

The file value can be a FITS file or a representation PNG file. The fits value will be the path of the FITS file associated with this image. For a presentation PNG file, the path generally will be relative to the JS9 install directory. For a normal FITS file, the path usually is an absolute path to the FITS file.

If you are calling JS9.GetImageData() from an external process (via the msg protocol), you almost certainly want to set dflag to "array". Doing so will serialize the data as an array instead of as an object, saving a considerable amount of transfer data.

Given a FITS-standard 1-indexed image pixel x,y, you can find the data value at that location using:

    val = obj.data[Math.floor(y-0.5) * obj.width + Math.floor(x-0.5)];
Note the need to integerize the x and y values: JavaScript arrays are objects and so floating point array indices do not get truncated automatically as in C. They will return null values.

Also note that since we need 0-based data array indexes, we subtract 1 from the 1-based image position. But then we must add 0.5 before rounding because by convention, x.0, y.0 is the middle of the pixel.

Get image data for all images loaded into the specified display

imarr = JS9.GetDisplayData()

returns:

The JS9.GetDisplayData() routine returns an array of image data objects, one for each images loaded into the specified display. That is, it returns the same type of information as JS9.GetImageData(), but does so for each image associated with the display, not just the current image.

Display an image

JS9.DisplayImage(step)

where:

The display steps are: "colors" (remake color data when cmap has changed), "scaled" (rescale data values), "primary" (convert scaled data values to color values), and "display" (write color values to the web page).

The default step is "primary", which displays the image without recalculating color data, scaled data, etc. This generally is what you want, unless you have explicitly changed parameter(s) used in a prior step.

Re-read the image data and re-display

JS9.RefreshImage(input, func)

where:

This routine can be used, for example, in laboratory settings where data is being gathered in real-time and the JS9 display needs to be refreshed periodically. The first input argument can be one of the following: When passing an object as input, the required image property containing the image data can be a javascript array or a typed data array. It also can contain a base64-encoded string containing an array. This latter can be useful when calling JS9.RefreshImage() via HTTP.

Ordinarily, when refreshing an image, there is no need to specify the optional axis, bitpix, or header properties. But note that you actually can change these values on the fly, and JS9 will process the new data correctly. Also, if you do not pass dmin or dmax, they will be calculated by JS9.

Note that you can pass a blob containing a complete FITS file to this routine. The blob will be passed to the underlying FITS-handler before being displayed. Thus, processing time is slightly greater than if you just pass the image data directly.

The main difference between JS9.RefreshImage() and JS9.Load() is that the former updates the data into an existing image, while the latter adds a completely new image to the display.

Extract and display a section of a FITS file

JS9.DisplaySection(opts)

where:

This routine allows you to extract and display a section of FITS file. The opts object contains properties specifying how to generate and display the section: All properties are optional: by default, the routine will extract a bin 1 image from the center of the file.

For example, if an image has dimensions 4096 x 4096, then specifying:

will bin the upper left 1024 x 1024 section of the image by 2 to produce a 512 x 512 image. Note that 0,0 can be used to specify the file center.

Table filtering allows you to select rows from an FITS binary table (e.g., an X-ray event list) by checking each row against an expression involving the columns in the table. When a table is filtered, only valid rows satisfying these expressions are used to make the image.

A filter expression consists of an arithmetic or logical operation involving one or more column values from a table. Columns can be compared to other columns or to numeric constants. Standard JavaScript math functions can be applied to columns. JavaScript (or C) semantics are used when constructing expressions, with the usual precedence and associativity rules holding sway:

  Operator                                Associativity
  --------                                -------------
  ()                                      left to right
  !  (bitwise not) - (unary minus)        right to left
  *  /                                    left to right
  +  -                                    left to right
  < <= > >=                               left to right
  == !=                                   left to right
  &  (bitwise and)                        left to right
  ^  (bitwise exclusive or)               left to right
  |  (bitwise inclusive or)               left to right
  && (logical and)                        left to right
  || (logical or)                         left to right
  =                                       right to left
For example, if energy and pha are columns in a table, then the following are valid expressions:
  pha > 1
  energy == pha
  pha > 1 && energy ≤ 2
  max(pha,energy) ≥ 2.5

NB: JS9 uses cfitsio by default (you can, but should not, use the deprecated fitsy.js), and therefore follows cfitsio filtering conventions, which are documented here.

Display an extension from a multi-extension FITS file

JS9.DisplayExtension(extid, opts)

where:

This routine allows you to display images and even binary tables from a multi-extension FITS file. (See, for example, the FITS Primer for information about HDUs and multi-extension FITS).

Display a slice of a FITS data cube

JS9.DisplaySlice(slice, opts)

where:

This routine allows you to display a 2D slice of a 3D or 4D FITS data cube, i.e. a FITS image containing 3 or 4 axes.

The slice parameter can either be the numeric value of the slice in the third (or fourth) image dimension (starting with 1) or it can be a slice description string: a combination of asterisks and a numeric value defines the slice axis. Thus, for example, in a 1024 x 1024 x 16 cube, you can display the sixth slice along the third axis in one of two ways:

  JS9.DisplaySlice(6);
or:
  JS9.DisplaySlice("*,*,6");
If the image was organized as 16 x 1024 x 1024, you would use the string description:
  JS9.DisplaySlice("6,*,*");

By default, the new slice replaces the data in the currently displayed image. You can display the slice as a separate image by supplying an opts object with its separate property set to true. For example:

  JS9.DisplaySlice("6,*,*", {separate: true});
will display the sixth slice of the first image dimension separately from the original file, allowing blinking, image blending, etc. between the two "files". Note that the new id and filename are adjusted to be the original file's values with the cfitsio image section [6:6,*,*] appended.

Blend the image in an image stack using W3C composite/blend modes

JS9.BlendImage(blendMode, opacity)

Calling sequences:

    JS9.BlendImage()                   # return current blend params
    JS9.BlendImage(true||false)        # turn on/off blending
    JS9.BlendImage(mode, opacity)      # set blend mode and/or opacity

where:

Image processing programs such as Adobe Photoshop and Gimp allow you to blend a stack of images together by mixing the RGB colors. The W3C has defined a number of composite and blending modes which have been implemented by Firefox, Chrome, and Safari (what about IE?): In addition, the following Porter-Duff compositing modes are available (though its unclear how useful they are in JS9 image processing): Blending and compositing modes are described in detail in this W3C candidate recommendation and in this Mozilla document.

JS9 allows you to use these modes to blend images together. If you load two images of the same object into JS9, you can use the JS9.ReprojectData() routine to align them by WCS. You then can blend one image into the other by specifying a blend mode and an optional opacity. For example, if chandra.fits and spitzer.fits are two aligned images of the same object, and chandra.fits is currently being displayed, you can blend spitzer into chandra using the "screen" blend and opacity 0.9 mode this way:

    JS9.BlendImage("screen", 0.9);
After the spitzer image is blended, both images will be displayed as part of the chandra.fits display. However, changing the colormap, scale, contrast, or bias will only affect the current chandra image, not the blended spitzer part. In this way, you can continue to manipulate the current image and the image blending will update automatically.

Also note that the spitzer image is still available separately for display and manipulation. You can switch to displaying spitzer and change colormap, scale, bias, contrast, etc. But since the images are now blended, changes to spitzer will be reflected in the spitzer part of the blended chandra display. Thus, if you change the colormap on the display of spitzer, and change back to chandra, the blended chandra image will utilize the new colormap.

This linkage is maintained during zoom and pan operations. If you display the blended chandra image and then zoom or pan it, both images will be updated correctly to maintain alignment. But note that this means when you go back to the spitzer display, its zoom and/or pan values will have been updated. In this way, the spitzer image always is correctly linked to the blended version. The JS9.BlendImage() call accepts a variable number of arguments to perform a variety of functions:

Other actions will be added as we gain experience with blending operations

Set the global image blend more for the specified display

mode = JS9.BlendDisplay(true|false)

returns:

This routine will turn on/off the global image blend mode for the specified display. If no argument is specified, it returns the current blend mode.

Clear the image from the display and mark resources for release

JS9.CloseImage()

Each loaded image claims a non-trivial amount of memory from a finite amount of browser heap space. For example, the default 32-bit version of Google Chrome has a memory limit of approximately 500Mb. If you are finished viewing an image, closing it tells the browser that the image's memory can be freed. In principle, this is can help reduce overall memory usage as successive images are loaded and discarded. Note, however, that closing an image only provides a hint to the browser, since this sort of garbage collection is not directly accessible to JavaScript programming.

Some day, all browsers will support full 64-bit addressing and this problem will go away ...

Get the image colormap

cmap = JS9.GetColormap()

returns:

The returned cmap object will contain the following properties:

Set the image colormap

JS9.SetColormap(colormap, [contrast, bias])

where:

Set the current colormap, contrast/bias, or both. This call takes one (colormap), two (contrast, bias) or three (colormap, contrast, bias) arguments.

Save current colormap definition

JS9.SaveColormap(filename)

where:

Save the current colormap definition for displayed image as a json file. If filename is not specified, the file will be saved as "js9.cmap". This is useful if you want to edit the current definition to make a new colormap.

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

Add a colormap to JS9

JS9.AddColormap(name, aa|rr,gg,bb|obj|json)

where:

You can add new colormaps to JS9 using one of two formats. The first is an array of RGB triplets (i.e. an array of 3-D arrays), where each triplet defines a color. The elements of the colormap are divided evenly between these 3-D triplets. For example, the i8 colormap is defined as:
    JS9.AddColormap("i8", [[0,0,0], [0,1,0], [0,0,1], [0,1,1], [1,0,0], [1,1,0], [1,0,1], [1,1,1]]));
Here, the colormap is divided into 8 sections having the following colors: black, green, blue, cyan (green + blue), red, yellow (red + green), purple (red + blue), and white. A colormap such as sls also utilizes an array of RGB triplets, but it has 200 entries, leading to much more gradual transitions between colors.

The second colormap format consists three arrays of vertices defining the change in intensity of red, green, and blue, respectively. For each of these three color triplets, the first coordinate of each vertex is the x-distance along the colormap axis (scaled from 0 to 1) and the second coordinate is the y-intensity of the color. Colors are interpolated between the vertices. For example, consider the following:

    JS9.AddColormap("red", [[0,0],[1,1]], [[0,0], [0,0]], [[0,0],[0,0]]);
    JS9.AddColormap("blue", [[0,0],[0,0]], [[0,0], [0,0]], [[0,0],[1,1]]);
    JS9.AddColormap("purple", [[0,0],[1,1]], [[0,0], [0,0]], [[0,0],[1,1]]);
In the red (blue) colormap, the red (blue) array contains two vertices, whose color ranges from no intensity (0) to full intensity (1) over the whole range of the colormap (0 to 1). The same holds true for the purple colormap, except that both red and blue change from zero to full intensity.

For a more complicated example, consider the a colormap, which is defined as:

    JS9.AddColormap("a",
      [[0,0], [0.25,0], [0.5,1], [1,1]],
      [[0,0], [0.25,1], [0.5,0], [0.77,0], [1,1]],
      [[0,0], [0.125,0], [0.5, 1], [0.64,0.5], [0.77, 0], [1,0]]);
Here we see that red is absent for the first quarter of the colormap, then gradually increases to full intensity by the half mark, after which it stays at full intensity to the end. Green ramps up to full intensity in the first quarter, then drops to zero by the half and stays that way until a bit more than three-quarters along, after which it gradually increases again. Blue starts off at no intensity for an eighth, then gradually increases to full intensity by the half-way mark, decreasing gradually to zero by the three-quarter mark. The result is that you see, for example, green at the beginning and yellow (red + green) at the end, with some purple (red + blue) in the middle of the colormap.

As a convenience, you also can pass an object or json string containing the colormap definition:

    # RGB color triplets for the I8 colormap in a "colors" property
    {"name":"i8","colors":[[0,0,0],[0,1,0],[0,0,1],[0,1,1],[1,0,0],[1,1,0],[1,0,1],[1,1,1]]}

    # all 3 vertex arrays for the purple colormap in one "vertices" property
    {"name":"purple","vertices":[[[0,0],[1,1]],[[0,0],[0,0]],[[0,0],[1,1]]]}

Finally, note that JS9.AddColormap() adds its new colormap to all JS9 displays on the given page.

Get RGB Mode

JS9.GetRGBMode()

returns:

The returned object will contain the following properties:

Set RGB Mode

JS9.SetRGBMode(mode, [imobj])

where:

In RGB mode, three images assigned the "red", "green", and "blue" colormaps are displayed as a single image. The RGB color of each displayed pixel is a combination of the "red", "green", and "blue" pixel value taken from the appropriate image. Note that all three images are not required: you can display an RGB image using two of the three colors simply by not assigning the third colormap.

The JS9.SetRGBMode() call turns on or off RGB mode. The boolean mode argument specifies whether to activate or de-activate RGB mode. The optional imobj object specifies (already-loaded) images to assign to the three colormaps:

If imobj is not specified, it is assumed that images have been assigned the "red", "green", and "blue" colormaps by another means. (Once again, it is not necessary to assign all three colormaps.)

If no arguments are specified, the current RGB mode is toggled;

Get the image scale

scale = JS9.GetScale()

returns:

The returned scale object will contain the following properties:

Set the image scale

JS9.SetScale(scale, smin, smax)

where:

Set the current scale, min/max, or both. This call takes one (scale), two (smin, max) or three (scale, smin, smax) arguments. (If "zscale" or "zmax" is specified, any supplied smin and smax values are ignored.)

Get the image zoom factor

zoom = JS9.GetZoom()

returns:

Get the zoom factor.

Set the image zoom factor

JS9.SetZoom(zoom)

where:

The zoom directives are:

Get the image pan position

ipos = JS9.GetPan()

returns:

The returned ipos object will contain the following properties:

Set the image pan position

JS9.SetPan(x, y)

where:

Set the current pan position using image coordinates. Note that you can use JS9.WCSToPix() and JS9.PixToWCS() to convert between image and WCS coordinates.

Get the display coordinates from an event

dpos = JS9.EventToDisplayPos(evt)

where:

returns: If you define your own event callbacks, you can use this routine to convert the event position to a display position, which can then be used to get the image position (see below).

Get the image coordinates from the display coordinates

ipos = JS9.DisplayToImagePos(dpos)

where:

returns: Note that image coordinates are one-indexed, as per FITS conventions, while display coordinate are 0-indexed.

Get the display coordinates from the image coordinates

dpos = JS9.ImageToDisplayPos(ipos)

where:

returns: Get display (screen) coordinates from image coordinates. Note that image coordinates are one-indexed, as per FITS conventions, while display coordinate are 0-indexed.

Get the image coordinates from the logical coordinates

ipos = JS9.LogicalToImagePos(lpos, lcs)

where:

returns: Logical coordinate systems include: "physical" (defined by LTM/LTV keywords in a FITS header), "detector" (DTM/DTV keywords), and "amplifier" (ATM/ATV keywords). Physical coordinates are the most common. In the world of X-ray astronomy, they refer to the "zoom 1" coordinates of the data file.

This routine will convert from logical to image coordinates. By default, the current logical coordinate system is used. You can specify a different logical coordinate system (assuming the appropriate keywords have been defined).

Get the logical coordinates from the image coordinates

lpos = JS9.ImageToLogicalPos(ipos, lcs)

where:

returns: Logical coordinate systems include: "physical" (defined by LTM/LTV keywords in a FITS header), "detector" (DTM/DTV keywords), and "amplifier" (ATM/ATV keywords). Physical coordinates are the most common. In the world of X-ray astronomy, they refer to the "zoom 1" coordinates of the data file.

This routine will convert from image to logical coordinates. By default, the current logical coordinate system is used. You can specify a different logical coordinate system (assuming the appropriate keywords have been defined).

Get value/position information

valpos = JS9.GetValPos(ipos, display)

where:

returns: This routine determines the data value at a given image position and returns an object containing the following information:

Set the value/position display mode

JS9.SetValPos(mode)

where:

Set the display mode of the value/position display for the specified image.

Get the image inherit mode

inherit = JS9.GetImageInherit()

returns:

The JS9.GetImageInherit() routine returns a boolean specifying whether a new image grabs the image params (e.g., colormap, scale, zoom, etc.) from the currently displayed image. If false, these params are taken from the default JS9.imageOpts object.

Set the image inherit mode

JS9.SetImageInherit(mode)

where:

The JS9.SetImageInherit() routine specifies whether a new image grabs the image params (e.g., colormap, scale, zoom, etc.) from the currently displayed image. If false, these params are taken from the default JS9.imageOpts object.

Get information about the current WCS

wcsobj = JS9.GetWCS()

returns:

Get information about the current WCS, including:

Set the current WCS

JS9.SetWCS(which)

where:

Set the current WCS, in cases where alternate WCS's are available. The alternate WCS convention is described in the standard paper by Greisen and Callebretta Representations of world coordinates in FITS (A&A 395, 1061–1075 (2002)). If a FITS file contains alternate WCS info, you can switch to an alternate using this routine or using the File->alternate wcs menu option.

The which argument can be one of the following:

If no argument is supplied, the default WCS is set up.

Get the current WCS units

unitsstr = JS9.GetWCSUnits()

returns:

Get the current WCS units.

Set the current WCS units

JS9.SetWCSUnits(unitsstr)

where:

Set the current WCS units.

Get the current World Coordinate System

sysstr = JS9.GetWCSSys()

returns:

Get current WCS system.

Set the current World Coordinate System

JS9.SetWCSSys(sysstr)

where:

Set current WCS system. The WCS systems are available only if WCS information is contained in the FITS header. Also note that "physical" coordinates are the coordinates tied to the original file. They are mainly used in X-ray astronomy where individually detected photon events are binned into an image, possibly using a blocking factor. For optical images, image and physical coordinate usually are identical.

Convert image pixel position to WCS position

wcsobj = JS9.PixToWCS(x, y)

where:

returns:

The wcs object contains the following properties:

Convert WCS position to image pixel position

pixobj = JS9.WCSToPix(ra, dec)

where:

returns: The pixel object contains the following properties:

Display a text message

JS9.DisplayMessage(which, text)

where:

The text string is displayed in the "info" area (usually occupied by the valpos display) or the "region" area (where regions are displayed). The empty string will clear the previous message.

Create or modify a raw data layer

JS9.RawDataLayer(opts, func)

where:

Each image has raw data associated with it, i.e. the underlying astronomical image pixels that are scaled and displayed using the chosen scale and colormap. You can manipulate the raw data by creating a new raw data layer using JS9.RawDataLayer(), setting the image pixel values, and then making this layer the current one. The original raw data (with id "raw0") will be maintained in separate layer, so you can switch between layers (also using this routine).

To create a new raw data layer (or edit an existing layer), call the JS9.RawDataLayer() with two arguments: layer opts (or layer name) and a function. The layer opts object can have the following properties:

Alternative, you can pass the id of the raw data layer as a string and use the defaults for the other properties (which usually is sufficient).

The pixel modifying function should have the following calling sequence:

    func(oraw, nraw, opts)
where: The function should return true is you want to switch to the new layer and display it, or false to discard the new layer (in case of an error).

Note that the nraw object will contain the raw data for this layer, if it already exists. Otherwise, it will contain a copy of the from data.

For example, the following routine creates a new "clip" layer and clips the original raw data at the specified nmax level:

    im.rawData({rawid: "clip", nmax: n}, function (oraw, nraw, opts){
        var i, len;
        opts = opts || {};
        if( opts.nmax === undefined ){ opts.nmax = 0; }
        len = nraw.width * nraw.height;
        for(i=0; i<len; i++){
            if( oraw.data[i] < opts.nmax ){
                nraw.data[i] = 0;
            } else {
                nraw.data[i] = oraw.data[i];
            }
        }
        return true;
    });
When clipping, the nraw pixel values are taken from the oraw values, so that you can clip to a value of 100, then clip to a value of 50, and get the right result. This is different from the following example "add" layer, which adds a constant value to the existing data:
    im.rawData({rawid: "add", val: n}, function (oraw, nraw, opts){
        var i, len;
        opts = opts || {};
        if( opts.val === undefined ){
            opts.val = 1;
        }
        len = nraw.width * nraw.height;
        for(i=0; i<len; i++){
            nraw.data[i] += opts.val;
        }
        return true;
    });
Here, the operation is performed on the existing "add" layer each time, so that the addition is cumulative.

The oraw and nraw objects contain a subset of the properties returned by JS9.GetImageData():

To switch to a layer, call JS9.RawDataLayer() with a single argument, the layer name:

    JS9.RawDataLayer("raw0")   # switch to original data
    JS9.RawDataLayer("clip")   # switch to clipped data
    JS9.RawDataLayer("add")    # switch to add data
To get the currently displayed layer, call the routine with no arguments:
    JS9.RawDataLayer()         # returns "clip"

Gaussian blur of raw data

JS9.GaussBlurData(sigma, opts)

where:

This routine creates a new raw data layer called "gaussBlur" in which the image pixel values are blurred using a Gaussian function with the specified sigma. The routine uses the fast Gaussian blur algorithm (approximating a full Gaussian blur with three passes of a box blur) described here.

Perform image arithmetic on raw data

JS9.ImarithData(op, arg1, opts)

where:

The JS9.ImarithData() routine performs basic arithmetic (addition, subtraction, multiplication, division, minimum, maximum, average) between the currently displayed image and either another image or a constant value. The first op argument is a string, as detailed above. The second arg1 argument can be a numeric value or an image id. In the former case, the constant value is applied to each pixel in the image. In the latter case, the operation is performed between the corresponding pixels in the two images. For example:
    JS9.ImarithData("max", "foo.fits");
will make a new data layer of the currently displayed image, where each pixel is the larger value from that image and the foo.fits image (which can be in any display).

This routine creates a new raw data layer called "imarith" containing the results of the operation. Successive calls to this routine are cumulative, so that you can build up a more complex operation from simple ones. For example:

    # foo.fits is displayed in the "myJS9" display
    var myim = JS9.GetImage({display: "myJS9"});
    JS9.ImarithData("max", myim);
    JS9.ImarithData("add", 2.718);
will make a new data layer where each pixel is the larger value from the two images, after which an approximation of the irrational number e is added to each pixel.

The special reset operation deletes the "imarith" raw data layer, allowing you to start afresh.

The bitpix value of the new "imarith" layer is chosen as follows:

You can override the choice of bitpix by passing a bitpix property in the optional opts object.

Finally, note that the two images must have the same dimensions. We might be able to remove this restriction in the future, although it is unclear how one lines up images of different dimensions.

Shift raw data

JS9.ShiftData(x, y, opts)

where:

This routine creates a new raw data layer called "shift" in which the pixels are shifted from the original image array by the specified amount in x and/or y. The results of successive shifts are cumulative. The routine is used by the Harvard-Smithsonian Center for Astrophysics MicroObservatory project interactively to align images that are only slightly offset from one another.

Apply a filter to the RGB image

JS9.FilterRGBImage(filter, args)

where:

In JS9, you can change the raw data (and hence the displayed image) using routines such as JS9.GaussBlurData() or the more general JS9.RawDataLayer(). You also can apply image processing techniques directly to the displayed RGB image without changing the underlying raw data, using this routine. The web has an overwhelming amount of information about image processing. A good technical article concerning the use of image filters with Javascript and the HTML5 canvas is available at: http://www.html5rocks.com/en/tutorials/canvas/imagefilters/

The JS9.FilterRGBImage() routine supports a number of image processing routines, which are listed below. To call one of them using JS9.FilterRGBImage(), supply the filter name, followed by any filter-specific arguments, e.g.:

    JS9.FilterRGBImage("luminance", {display: "myJS9"});
    JS9.FilterRGBImage("duotone", "g", {display: "myJS9"});
    JS9.FilterRGBImage("convolve", [-1,-1,-1,-1,8,-1,-1,-1,-1]);
You can, of course, use the default arguments where applicable.

Note that the standard JS9 colormaps, scale, contrast and bias selections are applied to the raw data to regenerate the RGB image. Thus, if you use any of the image processing techniques listed below and then change colormap, contrast, bias, or scale, you will undo the applied image processing. This is a good way to reset the displayed image. The same thing can be accomplished programmatically by specifying "reset" as the filter name:

    JS9.FilterRGBImage("reset", {display: "myJS9"});

The following simple image processing filters are available:

The following image convolutions are available:

With no arguments, the routine returns an array of available filters:

    JS9.FilterRGBImage()
    ["convolve", "luminance", ..., "blur", "emboss", "lighten", "darken"]

Reproject an image using a specified WCS

JS9.ReprojectData(wcsim, opts)

where:

JS9.ReprojectData() creates a new raw data layer (with default id of "reproject") in which the pixels are reprojected using the WCS from another image. The mProjectPP program from the Montage software suite is used to perform the reprojection. Please read the documentation on mProjectPP from the Montage web site, which includes this explanation:

    mProjectPP performs a plane-to-plane transform on the input image, and
    is an adaptation of the Mopex algorithm and developed in collaboration
    with the Spitzer Space Telescope. It provides a speed increase of
    approximately a factor of 30 over the general-purpose mProject. However,
    mProjectPP is only suitable for projections which can be approximated
    by tangent-plane projections (TAN, SIN, ZEA, STG, ARC), and is therefore
    not suited for images covering large portions of the sky. Also note that
    it does not directly support changes in coordinate system (i.e. equatorial
    to galactic coordinates), though these changes can be facilitated by the
    use of an alternate header.

The wcsim argument is an image id, image filename, or image object pointing to the WCS image.

The opts object can contain the following reproject-specific properties:

The cmdswitches will be prepended to the mProjectPP command line. For example:
 {cmdswitches: "-d 1 -z .75"}
will set the mProjectPP debugging and the drizzle factor, resulting in a command line that looks like this:
  mProjectPP -d 1 -z .75 -s statusfile in.fits out.fits template.hdr
See the mProjectPP documentation for more information about command switches.

Reprojection is an intensive process which can take a considerable amount of memory and processing time. To avoid crashes, we currently restrict the WCS image size used for reprojection to a value defined by JS9.REPROJDIM, currently 2300 x 2300. Even this might be too large for iOS devices under certain circumstances, although issues regarding memory are evolving rapidly.

Rotate an image around the WCS CRPIX point

JS9.RotateData(angle, opts)

where:

The JS9.RotateData() routine using JS9.ReprojectData() to rotate image data by the specified angle (in degrees). If the string "northup" or "northisup" is specified, the rotation angle is set to 0. The rotation is performed about the WCS CRPIX1, CRPIX2 point.

The optional opts object is passed directly to the JS9.ReprojectData() routine. See JS9.ReprojectData() below for more information.

Save an image session to a file

JS9.SaveSession(session)

where:

This routine saves all essential session information about the currently displayed image (filename, scaling, colormap, contrast/bias, zoom, regions, catalogs, etc) in a json-formatted file. You can subsequently load this file into JS9 to restore the image session.

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

The session file is a text file and can be edited, subject to the usual rules of json formatting. For example, you can change the colormap, scaling, etc. after the fact.

The session file contains a file property near the top that specifies the location of the image. A local file usually will contain an absolute path or a path relative to the web page being displayed. However, if the image was originally opened using drag-and-drop, no pathname information is available, in accordance with standard web security protocols. In this case, you must edit the session file to supply the path (either absolute or relative to the web page) before re-loading the session.

Load a previously saved image session from a file

JS9.LoadSession(session)

where:

Restore an image session by loading a json-formatted session file. The image itself is retrieved and loaded, and all of the saved parameters and graphics (scale, colormap, regions, catalogs etc) are applied to the display.

The session file contains a file property near the top that specifies the location of the image. A local file usually will contain an absolute path or a path relative to the web page being displayed. However, if the image was originally opened using drag-and-drop, no pathname information is available, in accordance with standard web security protocols. In this case, you must edit the session file to supply the path (either absolute or relative to the web page) before re-loading the session.

Note that the raw data file itself is not saved (only its pathname), so you must have access to that file in order to restore a session. However, the data file need not be in the same location as it was originally: you can adjust the path of the data file by editing the file property as needed.


Working with Regions

Spatial regions of interest are a crucial part of astronomical data analysis. The regions layer is a special case of the more generalized shape layers, automatically created by JS9 to support the options in the Regions menu, as well as local and server-side data analysis using regions.

The regions layer has the special property that, by default, its z-index is higher than other shape layers, so that regions are displayed on top of other shape layers.

Note that the GetRegions(), ChangeRegions(), RemoveRegions() calls all take a regions specification as the second argument, which can be any of the following (in order of precedence):

Thus, it is possible to act on multiple regions at the same time.

Add one or more regions to the regions layer

id = JS9.AddRegions(rarr, opts)

where:

returns: The rarr argument can be a region shape ("annulus", "box", "circle", "ellipse", "point", "polygon", "text"), a single region object, or an array of region objects. Region objects contain one or more properties, of which the most important are: Other available properties include: Deprecated properties: Here are some examples of ways to create regions:
    # example 1: circular region in the center of the field
    JS9.AddRegions("circle");

    # example 2: red circular region in the center of the field
    JS9.AddRegions("circle", {color: "red"});

    # example 3: red circular region with dashed lines
    JS9.AddRegions("circle", {color: "red", strokeDashArray: [3,1]});

    # example 4: regions using an object in first arg
    JS9.AddRegions({shape: "circle", color: "red", strokeDashArray: [3,1]})

    # example 5: multiple regions using an array specification in first arg
    JS9.AddRegions([{shape: "circle", color: "red", strokeDashArray: [3,1]},
                    {shape: "box", color: "green", strokeDashArray: [6,1]}])

    # example 6: js9 region syntax: properties in the second arg object
    JS9.AddRegions('ellipse(23:23:22.179, +58:48:10.542, 40", 20", 60)', {text: "ellipse test", color: "violet", tags: "json tag, another tag", textOpts: {color: "yellow", fontSize: 16, fontStyle: "italic", fontWeight: "bold"}});

    # example 7: js9 region syntax: json properties in the first arg string
    JS9.AddRegions('ellipse(23:23:22.179, +58:48:10.542, 40", 20", 60) {"color": "violet", "text": "ellipse test", "textOpts": {"color": "yellow", "fontSize": 16, "fontStyle": "italic", "fontWeight": "bold"}} # json tag, another tag');

    # example 8: ds9 region syntax: comment properties in the first arg string
    JS9.AddRegions('box(23:23:35.486, +58:50:03.146, 40", 20", 30) # width=4 text={box test} dash=1 color=red rotate=0 tag="test tag"')
In sum, you can specify a region using: Note the difference between examples 6 and 7: the former passes properties in an object (second argument), so the key names need not be quoted. The latter passes properties in a json string as part of the first string argument: here the key names must be quoted using double quotes, as per the json specification.

Examples 6 and 7 also show the text property, which allows you to associate a text string with a non-text region. JS9 will create a separate text region as a child of the original region. You can move this child text region around relative to the original region, change its angle, etc. The child will then maintain its new position relative to the original region as the latter is moved, resized, etc. You also can double-click on the text child to bring up its configuration dialog box and change its color, font, size, text string, etc.

Get information about one or more regions

rarr = JS9.GetRegions(regions)

where:

returns:

If the regions argument is not specified, it defaults to "all". You can specify "selected" to return information about the selected regions, or a tag value to return information about regions having that tag.

Each returned region object contains the following properties:

The image position can be used to access the image data returned by the JS9.GetImageData() routine:
    obj = JS9.GetImageData();
    xreg = JS9.GetRegions("selected")[0];
    val = obj.data[Math.floor(xreg.y-0.5) * obj.width + Math.floor(xreg.x-0.5)];

Note the need to integerize the x and y values: JavaScript arrays are objects and so floating point array indices do not get truncated automatically as in C. They will return null values.

Also note that since we need 0-based data array indexes, we subtract 1 from the 1-based image position. But then we must add 0.5 before rounding because by convention, x.0, y.0 is the middle of the pixel.

Change one or more regions

JS9.ChangeRegions(regions, opts)

where:

Change one or more regions. If the regions argument is not specified, it defaults to "all". You can specify "selected" to return information about the selected regions, or a tag value to change regions having that tag.

The opts object can contain the parameters described in the JS9.AddRegions() section. However, you cannot (yet) change the shape itself (e.g. from "box" to "circle"). See js9onchange.html for examples of how to use this routine.

Copy one or more regions to another image

JS9.CopyRegions(to, regions)

where:

Copy regions to a different image. If to is "all", then the regions are copied to all images. If regions is not specified, the default will be

Remove one or more regions from the region layer

JS9.RemoveRegions(regions)

where:

If the regions argument is not specified, it defaults to "all". You can specify "selected" to return information about the selected regions, or a tag value to remove regions having that tag.

Save regions from the current image to a file

JS9.SaveRegions(filename, which, layer)

where:

Save the current regions for the displayed image as a JS9 regions file. If filename is not specified, the file will be saved as "js9.reg".

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

If the which argument is not specified, it defaults to "all". You can specify "selected" to return information about the selected regions, or a tag value to save regions having that tag.

If the layer argument is not specified, it defaults to "regions", i.e. the usual regions layer. You can specify a different layer, e.g., if you want to save a catalog layer as a region file (since SaveCatalog() will save the data in table format instead of as regions).

Load regions from a file into the current image

JS9.LoadRegions(filename)

where:

Load the specified regions file into the displayed image. The filename, which must be specified, can be a local file (with absolute path or a path relative to the displayed web page) or a URL.


Working with Shape Layers

JS9 supports individual layers for drawing 2D graphics. The regions layer is a special case of a shape layer, created automatically by JS9. The Catalog plugin creates a separate layer for each catalog. You can define your own shape layer using the NewShapeLayer() call and then add geometric shapes to it.

Note that the JS9.GetShapes(), JS9.ChangeShapes(), JS9.RemoveShapes() calls all take a shape specification as the second argument, which can be any of the following (in order of precedence):

Thus, it is possible to act on multiple shapes at the same time.

Create a new shape layer

lid = JS9.NewShapeLayer(layer, opts)

where:

returns: This routine creates a new named shape layer. You can then, add, change, and remove shapes in this layer using the routines below. The catalogs displayed by the Catalog plugin are examples of separate shape layers.

The optional opts parameter allows you to specify default options for the new layer. You can set a default for any property needed by your shape layer. See JS9.Regions.opts in js9.js for example of the default options for the regions layer.

The JS9.Catalogs.opts object is also supplied as a possible default object for new shape layers. It differs from the JS9.Regions.opts object in that it does not define regions-specific processing (such as double-click to edit a region parameters). It also makes the new layer non-interactive: individual shapes cannot be moved, rotated, resized, or deleted, nor do they respond to events.

Starting with the JS9.Catalogs.opts object as a default, you can make the new layer interactive in a few different ways. The first way is to set the movable property in the opts object to true. This will permit individual shapes to be moved, rotated, resized and deleted. Shapes also will be movable and resizable as a group.

The second way is to supply one or more event callbacks as properties to the opts object:

When the associated mouse event occurs on a shape, these functions will be called with the following arguments: For example, to define mouseover and mousedown callbacks:
    opts.onmouseover = function(im, xreg, evt){
        console.log("mouseover: %s %s", im.id, xreg.data.tag);
    };
    opts.onmousedown = function(im, xreg, evt){
        console.log("mousedown: %s %s", im.id, xreg.data.tag);
Note that the shapes are still not movable unless you also set the movable property.

In addition to firing callbacks on events for individual shapes, you can set the ongroupcreate property in the opts object to a function that will fire when two or more objects are selected into a group (which is done using the Command key on a Mac, or Control key everywhere else):

The function will be called with the following arguments: Note that an array of xreg objects is passed in this case instead of the single "current" object passed in the other event callbacks. For example:
    opts.ongroupcreate = function(im, xregs, evt){
        var i, nshape, xcen, ycen;
        var xtot=0, ytot=0;
        nshape = xregs.length;
        for(i=0; i<nshape; i++){
          xtot += xregs[i].x; ytot += xregs[i].y;
        }
        xcen = xtot / nshape; ycen = ytot / nshape;
        console.log("average pos for %s objects: %s,%s", nshape, xcen, ycen);
    }

The final way to make a shape layer interactive is to specify a tooltip to display when hovering over objects in this shape layer. This is done by assigning a tooltip format string to the tooltip property of the opts object. This string can contain HTML directives, and it also can contain references to properties in the im, xreg, and evt objects. When the mouse hovers over an object, a tooltip string is generated by macro-expanding the values for these properties. The generated tooltip string is displayed as the inner HTML of the tooltip. When the mouse leaves the object, the tooltip is hidden.

For example, consider the following tooltip string:

    opts.tooltip = "<b>id: $im.id</b><br>pos: $xreg.x $xreg.y<br><i>$xreg.data.tag</i>";
Note how properties of the im and xreg objects are specified with a "$" prefix. When the mouse hovers over an object, the generated tooltip will display current image id in bold, followed by that object's x,y pixel position, followed by a user tag property passed in the data object when the shape was added.

Show or hide the specified shape layer

JS9.ShowShapeLayer(layer, mode)

where:

Shape layers can be hidden from display. This could be useful, for example, if you have several catalogs loaded into a display and want to view one at a time.

Make the specified shape layer the active layer

JS9.ActiveShapeLayer(layer)

where:

returns: For a given image, one shape layer at a time is active, responding to mouse and touch events. Ordinarily, a shape layer becomes the active layer when it is first created and shapes are added to it. Thus, the first time you create a region, the regions layer becomes active. If you then load a catalog into a catalog layer, that layer becomes active.

If no arguments are supplied, the JS9.ActiveShapeLayer() routine returns the currently active layer. Specify the name of a layer as the first argument to make it active. Note that the specified layer must be visible.

This routine forms the basis for the Shape Layer plugin, which provides a graphical way to make a layer active (by moving it to the top of the layer stack).

Add one or more shapes to the specified layer

JS9.AddShapes(layer, sarr, opts)

where:

returns: The sarr argument can be a shape ("annulus", "box", "circle", "ellipse", "point", "polygon", "text"), a single shape object, or an array of shape objects. Shape objects contain one or more properties, of which the most important are: Other available properties include: Deprecated properties:

Remove one or more shapes from the specified shape layer

JS9.RemoveShapes(layer, shapes)

where:

Get information about one or more shapes in the specified shape layer

JS9.GetShapes(layer, shapes)

where:

returns:

Each returned shape object contains the following properties:

Change one or more shapes in the specified layer

JS9.ChangeShapes(layer, shapes, opts)

where:

Change one or more shapes. The opts object can contain the parameters described in the JS9.AddShapes() section. However, you cannot (yet) change the shape itself (e.g. from "box" to "circle").

Load an astronomical catalog

JS9.LoadCatalog(layer, table, opts)

where:

Astronomical catalogs are a special type of JS9 shape layer, in which the shapes have been generated from a tab-delimited text file of columns, including two columns that contain RA and Dec values. An astronomical catalog can have a pre-amble of comments, which, by default, have a '#' character in the first column.

Two examples of catalog files are shown below:

  ra        	dec        	magj	magh	magk
  -----------	------------	------	----	----
  23:22:56.003	58:44:45.429	15.612	15.103	14.9
  23:22:56.230	58:45:32.011	13.723	13.174	12.981
  23:22:56.319	58:45:08.954	14.212	13.119	12.723
  ...

  #
  #   VizieR Astronomical Server vizier.u-strasbg.fr
  # ...
  #Coosys	J2000:	eq_FK5 J2000
  # ...
  #Title: 2MASS All-Sky Catalog of Point Sources (Cutri+ 2003)
  # ...
  #Column	_RAJ2000	(A12)		[ucd=pos.eq.ra]
  #Column	_DEJ2000	(A12)		[ucd=pos.eq.dec]
  #Column	RAJ2000	(F10.6)		[ucd=pos.eq.ra;meta.main]
  #Column	DEJ2000	(F10.6)		[ucd=pos.eq.dec;meta.main]
  #Column	2MASS	(a17)		[ucd=meta.id;meta.main]
  # ...
  #Column	Aflg	(I1)		[ucd=meta.code]
  _RAJ2000	_DEJ2000	RAJ2000	DEJ2000	2MASS	Jmag	e_Jmag	Hmag	  e_Hmag	Kmag	e_Kmag	Qflg	Rflg	Bflg	Cflg	Xflg	Aflg
  ------------	------------	----------	----------	-----------------	------	------	------	------	------	------	---	---	---	---	-	-
  23 22 56.002	+58 44 45.43	350.733342	+58.745953	23225600+5844454 	15.612	 0.064	15.103	 0.078	14.900	 0.121	AAB	222	111	ccc	0	0
  23 22 59.647	+58 44 56.84	350.748531	+58.749123	23225964+5844568 	15.580	 0.063	14.922	 0.072	14.506	 0.079	AAA	222	111	c00	0	0
  23 22 56.981	+58 44 44.61	350.737422	+58.745724	23225698+5844446 	14.920	 0.049	14.292	 0.049	14.002	 0.055	AAA	222	111	ccc	0	0
  ...
Notice that both files have a tab-delimited header, followed by a tab-delimited line of dashes, followed by tab-delimited data rows. This file format is sometimes called "tsv" or "tab-separated values". A very useful astronomical package for manipulating tables is Starbase, written by John Roll.

The JS9.LoadCatalog() routine will read a file in this format, processing the data rows by converting the RA and Dec values into image position values that will be displayed as shapes in a new catalog layer.

The first argument to the JS9.LoadCatalog() routine is the name of the shape layer that will contain the objects in the catalog. Specifying the name of an existing layer is valid: previous shapes in that layer will be removed.

The second argument is either a blob or a string containing the table data described above. Blobs are the result of loading a local file into the browser (e.g., the load catalog menu option), while strings result from a remote XHR call (e.g., the Archives and Catalogs plugin). Alternatively, the second string argument can be a file name or URL to load. File names are distinguished from strings containing a table in that the latter must contain at least one tab.

The third argument is an optional object used to specify parameters, including:

Most of these properties have default values that are stored in the JS9.globalOpts.catalogs object: manipulate that object as needed. The values listed above also can be changed by users via the Catalog tab in the Preferences plugin.

Note the second (Vizier) example table above does not actually have a columns called "RA" and "Dec". Instead, it has "_RAJ2000", "_DEJ2000", "RAJ2000", and "DEJ2000" columns. In order to pick RA and Dec column, a heuristic is used based on the values contained in the JS9.globalOpts.catalogs.ras and JS9.globalOpts.catalogs.decs arrays:

The default global JS9.globalOpts.catalogs.ras contains:
  ras:  ["RA", "_RAJ2000", "RAJ2000"]
  decs: ["Dec", "_DEJ2000", "DEJ2000"]
These defaults make it easy to process simple catalogs and Vizier catalogs.

Once the RA and Dec columns are specified, the RA and Dec values from each row are converted into image positions, and JS9 shapes are generated to be displayed in the new catalog layer. The WCS system used to convert RA and Dec into image coordinates can be specified using the opts.wcssys or JS9.globalOpts.catalogs.wcssys properties.

The RA and Dec of the catalog object are always saved in the data object of each generated shape, for use in the tooltip string. In addition, if opts.save (or the default: JS9.globalOpts.catalogs.save) property is not false, all of the catalog object properties will also be save in the data object for use in the tooltip string, e.g.:

  opts.tooltip = "$xreg.data.ra $xreg.data.dec $xreg.data.Qflg $xreg.data.Rflg"
in the above VizieR example. Caveat: any "." (dot) in any catalog property name will be converted to "_" (underscore) automatically. For a more detailed discussion of tooltips, please see JS9.NewShapeLayer().

When catalog layer has been created, you can use the Shape Layers plugin to toggle its visibility and also to change its position in the layer stack.

Save an astronomical catalog to a file

JS9.SaveCatalog(filename, which)

where:

Save the specified catalog-containing layer as a text file. If filename is not specified, the file will be saved as [layer].cat.

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

If the which argument is not specified, the catalog associated with the current active layer will be saved. In either case, the layer to save must actually be a catalog created from a tab-delimited file (or URL) of catalog objects (not, for example, the regions layer).


Mouse/Touch Gestures

JS9 supports the following configurable mouse and touch actions:

The initial actions are defined by the JS9.globalOpts.mouseActions and JS9.globalOpts.touchActions arrays. In addition, the JS9.globalOpts.mousetouchZoom property specifies whether a mouse wheel or touch/pinch will zoom the image. The default actions are: where the array elements map to an increasing numbers of buttons/fingers. You can change the default actions of the elements in either array in your js9Prefs.json file. For example, to make pan and zoom the default one-touch and pinch actions respectively: As usual, the defaults in js9Prefs.json are used by all JS9 displays in a web page.

Users can change the action assignments using the MouseTouch plugin, which displays the current mapping between mouse/touch gesture and its action. Simply drag and drop an action to a different gesture in order to change the mapping. You also can toggle the scroll/pinch to zoom capability by clicking its button.

Web page developers can extend the available mouse and touch actions beyond the defaults contained in the Mouse Touch plugin. To add a new action, first write a function with the following calling sequence:

    gestureFunc(im, ipos, evt)
where: Add this new function to the JS9.MouseTouchActions array, using a descriptive name as its array index, e.g.:
    JS9.MouseTouch.Actions["all contrast/bias"] = function(im, ipos, evt){
      var i, myim;
      for(i=0; i<JS9.displays.length; i++){
        myim = JS9.displays[i].image;
        if( myim ){
          JS9.MouseTouch.Actions["change contrast/bias"](myim, ipos, evt);
        }
      }
    };
At this point, the Mouse Touch Plugin will display the new function in its list of available actions. Users can move it into one of the defined gestures to activate it.

Of course, you can add the new action to the JS9.globalOpts.mouseActions and/or JS9.globalOpts.touchActions arrays to make it active immediately:

    JS9.globalOpts.mouseActions[1] = "all contrast/bias";
    JS9.globalOpts.touchActions[1] = "all contrast/bias";
Here, we have replaced the one-button mouse move and two-button touch move actions (whose defaults are to "change contrast/bias") with our new action.


Server-side Analysis

Run a simple server-side analysis task

JS9.RunAnalysis(name, parr, func)

where:

The JS9.RunAnalysis() routine is used to execute a server-side analysis task and return the results for further processing within the web page. The optional parr array of parameters is passed to the JS9 analysis macro expander so that values can be added to the command line. The array is in jQuery name/value serialized object format, which is described here:
    http://api.jquery.com/serializeArray/

The func() routine is a callback function to process the returned results from the analysis task. The calling sequence is:

    func(stdout, stderr, errcode, aobj)
where: Typically, you would check stderr string first and issue an error message if there is an error. Otherwise, the stdout string can be processed based on the return type (rtype) property of the output (e.g., "text" or "plot"). For plotting, you can use flot functionality already loaded into JS9, or you can use your own chosen plotting package.

If no func callback is specified, the default processing will display "text" in a new light window. If the return type is "plot", the results are assumed to be in flot format and will be plotted.

Instead of passing a callback function each time, you can set the global property JS9.globalOpts.analysisFunc to a function having the same signature:

    func(stdout, stderr, errcode, aobj)
The specified function will be called for each execution of JS9.RunAnalysis

In addition, you can change the target of the analysis results display from a light window to your own div element by setting the global property JS9.globalOpts.analysisDiv to the id of your new target div. The div must have CSS height and width properties if you are going to plot results.

Run a server-side analysis task, utilizing parameters in a form

JS9.SubmitAnalysis(el, name, func)

where:

The JS9.SubmitAnalysis() routine is used to run an analysis task with input parameters from a form. Typically used as the Run button action in a form, it automatically serializes the form values and passes them to the JS9 analysis macro expander so that these values can be integrated into the analysis command line. See js9analysis.html for a simple example.

The func callback and global options are the same as for JS9.RunAnalysis() above.


Working with Displays

Lookup a JS9 display

dobj = JS9.LookupDisplay(dname, mustExist)

where:

returns: The JS9.LookupDisplay() routine returns the display object associated with the specified dname parameter. If no dname is specified, the first available display is returned.

If dname is specified but does not exist, an error is thrown unless the mustExist parameter is explicitly set to false (i.e., the default is that the id must exist). Resize the JS9 Display

JS9.ResizeDisplay(dname, width, height, opts)

where:

You can resize the JS9 display element by supplying new width and height parameters. The div on the web page will be resized and the image will be re-centered in the new display. If the display size has been increased, more of the image will be displayed as needed (up to the new size of the display). For example, if the original display was 512x512 and you increase it to 1024x1024, a 1024x1024 image will now be displayed in its entirety.

If the first argument is full, the display is resized to match the browser window.innerWidth and window.innerHeight variables, which are the width and height (in pixels) of the browser window viewport. You can then scroll the window so that the image fills the entire browser window. Alternatively, if the first argument is reset, the display is resized to match its original size.

The opts object can contain the following properties:

The default for resizeMenubar is true, so you only need to pass this property if you do not want to perform the resize.

If no arguments are passed to this routine, it returns an object containing the current display width and height. Otherwise, the display object is returned.

You can supply a display name as the first argument, or the display object:

    JS9.ResizeDisplay("myJS9", 350, 400);
or:
    JS9.ResizeDisplay(350, 400, {display: "myJS9"});

Move an image to a new JS9 display

JS9.MoveToDisplay(dname)

where:

The JS9.MoveToDisplay() routine moves the current image to the specified display:
    JS9.MoveToDisplay("myJS9", {display: "JS9"});
will move the current image displayed in the "JS9" display window to the "myJS9" window.

Note that the new JS9 display must already exist. New displays can be created with the JS9.LoadWindow() public access routine or the File:new JS9 light window menu option.

Gather other images to this JS9 Display

JS9.GatherDisplay(dname)

where:

This routine move all images in other displays to this display. You can supply a display name or the display object:

    JS9.GatherDisplay("myJS9");
or:
    JS9.GatherDisplay({display: "myJS9"});

Separate images in this JS9 Display into new displays

JS9.SeparateDisplay(dname)

where:

This routine moves each image in this display to a new display. You can supply a display name or the display object:

    JS9.SeparateDisplay("myJS9");
or:
    JS9.SeparateDisplay({display: "myJS9"});

Scroll the JS9 display to the center of the viewport

JS9.CenterDisplay(dname)

This routine scrolls this display to the center of the viewport. You can supply a display name or the display object:

    JS9.CenterDisplay("myJS9");
or:
    JS9.CenterDisplay({display: "myJS9"});


Miscellaneous

Print an image

JS9.Print(opts)

where:

Print the currently displayed image. A new window is displayed containing the image, along with regions and other graphical layers (the 2D graphics having been converted to a re-scalable format). The standard Print dialog box also is displayed and can be used to print this new window. Dismiss both windows when you are finished.

By default, if a colorbar is active on the page, it will be placed beneath the image. You can pass the colorbar: false option in the opts object to avoid printing the active colorbar.

Save image as a FITS file

JS9.SaveFITS(filename)

where:

Save the currently displayed image as a FITS file. If filename is not specified, the file will be saved as "js9.fits".

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

Save image as a PNG file

JS9.SavePNG(filename)

where:

Save the currently displayed image as a PNG file. If filename is not specified, the file will be saved as "js9.png". The image is saved along with the graphical overlays (regions, etc.).

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

Save image as a JPEG file

JS9.SaveJPEG(filename, quality)

where:

Save the currently displayed image as a JPEG file. If filename is not specified, the file will be saved as "js9.jpeg". The image is saved along with the graphical overlays (regions, etc.). If quality parameter is not specified, a suitable default is used. On FireFox (at least), this default values is 0.95 (I think).

Don't forget that the file is saved by the browser, in whatever location you have set up for downloads.

Get FITS header as a string

JS9.GetFITSHeader(nlflag)

where:

Return the FITS header as a string. By default, the returned string contains the 80-character FITS cards all concatenated together. If nlflag is true, each card will have a new-line appended.

Note that the JS9.GetImageData() routine also returns the FITS header, but as an object whose properties contain the header values. For example, obj.SIMPLE will usually have a value of true, obj.BITPIX will have contain the bits/pixel, etc. This object is more useful for programming tasks, but does not contain the FITS comments associated with each header card.

Display help in a light window

JS9.DisplayHelp(name)

where:

The help file names are the property names in JS9.helpOpts (e.g., "user" for the user page, "install" for the install page, etc.). Alternatively, you can specify an arbitrary URL to display (just because).

Display plugin in a light window

JS9.DisplayPlugin(name)

where:

Toggle the light-window display of the named plugin, as is done by the View and Analysis menus. That is, if the plugin is not visible, make it visible. If the plugin is visible, hide it.

You can supply the full class and plugin name or just the name, using exact case or lower case, e.g.:

As with plugins in the View and Analysis menus, this routine does nothing if the plugin is explicitly defined on the web page.

This routine is useful if you are building a web interface that supports the JS9 menu functions.

Use the file dialog box to load a FITS file

JS9.OpenFileMenu()

Calling this routine brings up the browser's file dialog box. When a FITS file is selected, if will be loaded into the JS9 display.

This routine is useful if you are building a web interface that supports the JS9 menu functions.

Use the file dialog box to load a region file

JS9.OpenRegionsMenu()

Calling this routine brings up the browser file menu. When a region file is selected, if will be loaded into the JS9 display.

This routine is useful if you are building a web interface that supports the JS9 menu functions.

Use the file dialog box to load a colormap file

JS9.OpenColormapMenu()

Calling this routine brings up the browser file menu. When a json-format colormap file is selected, it will be loaded into the JS9 display. The colormap file can take one of two forms (without the comments):

    # RGB color triplets for the I8 colormap in a "colors" property
    {"name":"i8","colors":[[0,0,0],[0,1,0],[0,0,1],[0,1,1],[1,0,0],[1,1,0],[1,0,1],[1,1,1]]}

    # all 3 vertex arrays for the purple colormap in one "vertices" property
    {"name":"purple","vertices":[[[0,0],[1,1]],[[0,0],[0,0]],[[0,0],[1,1]]]}
This routine is useful if you are building a web interface that supports the JS9 menu functions. See JS9.AddColormap() for more information about colormap formats.

Get location of JS9 installation directory

rpath = JS9.InstallDir(file)

where:

returns: Sometimes a plugin needs to load an auxiliary file inside the plugins sub-directory. The web page loading the plugin has an arbitrary location relative to the JS9 install directory, so this routine returns a relative path to the js9 install directory.

Send a message to a back-end server

JS9.Send(msg, obj, cb)

where:

JS9 sends various internal messages to the back-end server using either socket.io protocol (in the Node.js implementation) or CGI. For example, when a remote analysis call is made, JS9 sends a message to the back-end server detailing the task to call, parameters to pass, etc. Results are then returned to JS9 for display. JS9 also sends messages to the back-end server when a new image is displayed, when a FITS file is to be converted into a PNG representation file, etc.

Communication with the back-end is usually done behind the scenes and need not concern users or application programmers. However, if you write your own socket.io-based server, you might want to add project-specific messages to your implementation. For example, a Perl-based or Python-based server might add its own special messages that execute Perl or Python commands within the server in response to JS9 messages. In this case, you can use JS9.Send() to send a message to these message handlers.

The msg name is the name of the message, as defined by the server. By convention, an object is usually passed to the message handler. JS9 will add a dataPath property to this object to indicate the current list of directories in which to search for data. All other properties are specific to the message being handled. You can pass a null instead of an object and JS9.Send() will generate a temporary object to hold the dataPath.

The cb function will be called if the message sends an acknowledgment. The arguments passed to this function call by the server are specific to the message being handled.

For example, you can send a message to the back-end server to retrieve the list of available analysis tasks and then display this list using the call:

    JS9.Send("getAnalysis", null, function(s){alert(s)});
The "getAnalysis" message passes no parameters to the server. The server returns a list of available analysis tasks in json format.

Add a JS9 display div and/or associated plugins

JS9.AddDivs(id1, id2, ...)

where:

You can add new JS9 displays and/or plugins dynamically. To do this, you create the new divs, add them to the web page, and then call this routine to incorporate them into JS9.

The routine will accept a list of JS9 display divs to initialize. If all you are doing is adding one or more plugins to an existing display, leave the argument list empty.

For example, to add a new JS9 display and menubar at the end of a web page and load an image into that display:

    var html = "<div class='JS9Menubar' id='myJS9Menubar'></div><div class='JS9' id='myJS9'></div>";
    // jquery append to end of page
    $(html).appendTo($("body"));
    // create the new JS9 display, with associated plugins
    JS9.AddDivs("myJS9");
    // just a standard load to that display
    JS9.Load("foo.fits", {scale: "log"}, {display: "myJS9"});
See also JS9.LoadWindow() for a nice way to load images into a light-weight or completely new window.

Instantiate plugins on this web page

JS9.InstantiatePlugins()

Normally, JS9 will instantiate all of its plugins automatically once the page is loaded and ready: internally, jQuery $(document).ready() calls JS9.init(). However, module loaders such as Require.js can load scripts asynchronously and cause jQuery $(document).ready() to fire before all JS9 scripts are available. The RegisterPlugin() routine should deal properly with this situation. But just in case ... the JS9.InstantiatePlugins() will perform the plugin instantiation explicitly.

In you find you need to call this routine in order make your in-page plugins display properly, please let us know the circumstances.


Prototype Routines (not ready for prime time)

NB: The routines in this section are prototypes and therefore are subject to change. Feel free to contact us to discuss your needs so that we can gain a better understanding of what is required in these cases.

Load an auxiliary file

JS9.LoadAuxFile(name, func)

where:

It sometimes is desirable to process auxiliary files when an image is first loaded. Examples include overlaying an image mask on an image, and pre-loading regions from a region file. The JS9.LoadAuxFile prototypes this sort of functionality.

Auxiliary files are defined in the js9Prefs.json file by means of the JS9.auxfiles array containing one or more auxfile objects. Each auxfile specification has the following properties:

For example, the following defines two auxfiles:
    "auxFiles":   [{"type":	     "mask",
  		    "name":	     "sciencemasks",
		    "image": 	     "casa.*.png",
		    "url":	     "../myauxfiles/casa-mask.png"},
		   {"type":	     "regions",
		    "name":	     "scienceregions",
		    "image": 	     "casa.*.png",
		    "url":	     "../myauxfiles/casa.reg"}],
Here, a mask auxfile and a regions auxfile are defined for Cas-A dataset(s). These auxfiles are brought into play as follows:
    // run this routine after loading each image
    function onImageLoad(im){
      JS9.LoadAuxFile("sciencemasks", function(im, aux){
        // if we succeed in loading the mask, set up the onchange callback
        im.onregionschange = regionOnChange;
        // view the image through the mask data
        im.maskData = aux.im.raw.data;
        // I mean now!
	im.displayImage("all");
      });
    JS9.LoadAuxFile("scienceregions");
  }

  // tell JS9 about the onload callback
  JS9.imageOpts.onload = onImageLoad;
An onImageLoad function is defined that will try to load auxfiles of types "sciencemasks" and "scienceregions". This routine is set to be called whenever a new image is loaded.

On image load, when JS9.LoadAuxFile() is called, it looks in the list of known auxfiles for an entry that matches both the class name and the image name. If found (in the example above, for Cas-A datasets), the associated auxfile URL is loaded and type-specific processing is performed:

Once the initial auxfile processing has been performed, the optional callback function will be called. For the "mask" example above, the onregionchange property is set to call a JavaScript function (not shown here) whenever a region changes. The maskData property is set to point to the raw data of the auxfile (i.e., aux.im.raw.data). Finally, the image is re-displayed. Setting the maskData property and redisplaying the image causes the auxfile's data to be displayed as an opaque overlay on top of the currently displayed image.

As should be clear from the description above, considerable "black magic" currently is involved when processing auxiliary files. Visibility is required into the inner properties of the image object. Please contact us if you want to work with auxiliary files, so that we can continue to think about what is required for their support.


API Change History

The JS9 Public API is meant to be stable and well-documented. If we are forced to make an incompatible change to the API, it will be documented here.

20151218: OpenFileMenu and OpenRegionsMenu don't require a display argument

The routines OpenFileMenu() and OpenRegionsMenu() required a display argument, instead of utilizing the standard optional display object. This mistake has been corrected, with the result that both routines now target the default display, as expected, if no display is passed.

20141117: The Set routines now return "OK" instead of true on success

The public Set routines (SetZoom(), SetColormap(), etc) were returning a boolean true when successful. This has been changed to "OK", to make it clear that the return value is a status value, not a boolean data value.

20141028: callback function to RunAnalysis and SubmitAnalysis

Due to an oversight, the signature of the callback function supplied to RunAnalysis() (and its derivative function, SubmitAnalysis()) was missing the errcode argument. To correct this mistake, the signature was changed from:

    func(stdout, stderr, aobj)
to:
    func(stdout, stderr, errcode, aobj)
See js9onchange.html for an example of using this callback function.


Last updated: February 2, 2017