### External Communication with JS9

External communication with a JS9 display is supported by the js9 script, which is supplied with the JS9 source code. Alternatively, if you have installed a pre-built JS9 app (js9app or Voyager), you can export a messaging script using the File->export messaging script menu option. The script will be called js9msg for js9app and vger for Voyager. Note that it is not strictly necessary to install Node.js or Electron.js to send external messages: the js9 script utilizes wget or curl under the hood.

To display the js9 script options, type:

      sh> js9 --help

The js9 script supports two types of syntax:

- console: a lightweight set of commands from the JS9 Console plugin
- public api: all of the routines available in the JS9 Public API

The console syntax offers a restricted set of commands suitable for quick interactive use. Results are returned as simple strings:

      sh> js9 cmap heat        # change colormap to heat
      OK
      sh> js9 cmap             # return the current colormap
      heat

The console commands include:

      analysis	list/run analysis for current image (run)
      colormap	set/get colormap for current image (cmap)
      colormaps	get list of available colormaps (cmaps)
      global	set/get a JS9.globalOpts parameter
      grid		set/get coordinate grid for current image
      help		get list of available commands
      helper	set/get helper connection
      image		get name of current image or display specified image
      images	get list of currently loaded images
      load		load image(s)
      pan		set/get pan location for current image
      pix2wcs	get image pixel value for specified wcs position
      print		print image window
      refresh	refresh image using specified file (def: use last file)
      regcnts	counts in regions for current image
      regions	add or list region(s) (reg, region)
      resize	set/get display size for current image
      scale		set/get scaling for current image
      scales	get list of available scales
      section	display section of current image
      status	get status for specified (or current) image
      url		display a url
      wcssys	set/get wcs system for current image
      wcsu		set/get wcs units used for current image
      wcssystems	get list of available wcs systems
      wcsunits	get list of available wcs units
      wcs2pix	get wcs position for specified image pixel
      zoom		set/get zoom for current image

The JS9 Public API syntax offers the full power of the public API. It passes and returns JSON-formatted strings with a richer set of options than is available via the console syntax:

      # add a red circle region
      sh> js9 AddRegions circle '{"color":"red", "tags": "foo"}'
      # change color of all regions to violet and change the tags too
      sh> js9 ChangeRegions all '{"color":"violet","tags":"goo"}'
      # change color of selected regions to violet and change the tags too
      sh> js9 ChangeRegions selected '{"color":"violet","tags":"goo"}'
      # change color of red regions to violet
      sh> js9 ChangeRegions red '{"color":"violet"}'
      # change color of regions with "source" tag to violet
      sh> js9 ChangeRegions source '{"color":"violet"}'
      # get colormap
      sh> js9 GetColormap
      {"colormap":"grey","contrast":"3","bias":"0.8"}
      # set colormap
      sh> js9 SetColormap viridis 1 0.5
      # get scale parameters
      sh> js9 GetScale
      {"scale":"log","scalemin":0,"scalemax":51}
      # set scale in a different display
      sh> js9 SetScale linear '{"display":"myJS9"}'

Passed arguments are the same as the public API arguments, except that objects are passed as JSON strings. Returned objects are also in JSON format. Note in the last example above that the display object argument of the public API calls is supported. See JS9 Public API for more information.

An example of the use of the public API is given in the shell script below, where we load the Chandra image of the Kes 75 supernova remnant, display three energy cuts as separate images, assign red, green, and blue colormaps to the three energy cuts, and then blend them into a single display:

      #!/bin/bash
      # Chandra event file of the Kes75 SNR
      file="kes75/kes75_evt2.fits.gz"

      # some Chandra energy filters
      filter[1]="energy=500:1500"
      filter[2]="energy=1500:2500"
      filter[3]="energy=2500:8000"

      # RGB colormaps
      cmap[1]="red"
      cmap[2]="green"
      cmap[3]="blue"

      # load event file
      echo "loading event file: $file"
      js9load $file
      # turn blending off (since default is on)
      js9 BlendImage false

      # process each of the event filters
      for i in {1..3}; do
        echo "filter: ${filter[$i]}"
        # generate section using event filter into a separately displayed image
        js9 DisplaySection '{"filter":"'${filter[$i]}'","separate":true}'
        # this actually takes a bit of time
        sleep 4
        j=`expr $i + 1`
        # the new file with have a <n> string appended to the original id
        id="kes75_evt2.fits.gz[EVENTS]<$j>"
        # set image scale and assign one of the RGB colormaps
        js9 SetScale "log"            '{"display":"'$id'"}'
        js9 SetColormap "${cmap[$i]}" '{"display":"'$id'"}'
      done

      # blend all the images
      js9 BlendDisplay true
      echo "all done!"

Note the use of the js9load script to load an image. This auxiliary script uses the Load and GetLoadStatus API calls to load an image and wait for completion before returning. The default wait time is 10 seconds, but is configurable on the command line.

The rules governing whether a js9 script can talk to a JS9 instance are determined by the globalOpts.remoteMsgs property supplied to the back-end helper at start-up. The options are:

- < 0: no external communication is allowed
- ≥ 0: localhost can send to localhost
- ≥ 1: and same host can send to same host
- ≥ 2: and localhost can send to any host
- ≥ 3: and any host can send to any host

The default is 1: a message from a given host can be sent to instances started on that same host.

If a JS9 instance is connected to a helper on a remote host, you can use the --host or --helper switch to specify the remote host to contact. For example, if the helper is running on js9.cfa.harvard.edu, you can send a command to your instance of JS9 this way:

      sh> js9 --host https://js9.si.edu:443 region circle
      OK

(Note that the main JS9 web site uses a reverse proxy to communicate with an internal-facing JS9 helper. You send a message to the main web server port 443, and it is relayed to the helper listening internally on port 2718.)

If more than one instance of JS9 appears on a single web page, the --id switch can differentiate between instances. The value of the id switch is the div id for that JS9 instance. For example, if two instances of JS9 having div ids of "JS9" and "myJS9" are defined on the same page, then js9 can communicate with the latter in this way:

      sh> js9 --id myJS9 region circle
      OK
      sh> js9 --id myJS9 region
      ICRS; circle(23:23:26.929, +58:48:50.381, 14.76")

or, from the readline loop:

      sh> js9 --id myJS9
      JS9> region circle(23:23:26.929, +58:48:50.381, 14.76")
      OK
      JS9> region
      ICRS; circle(23:23:26.929, +58:48:50.381, 14.76")

Putting the last two techniques together, you can talk to one of many JS9 instances on a page connected to a remote helper this way:

      sh> js9 --host https://js9.si.edu:443 --id myJS9 cmap heat
      OK

NB: The following options are available with the canonical js9 script, but not with the script that is exported from a pre-built JS9 app using the File->export messaging script menu option. These options require you to have installed the Node.js or Electron.js, since they call js9Msg.js under the hood.

If the -p or --pipe switch is supplied, the script will read commands from stdin. You can send multiple commands to the script's standard input (comments and blank lines are ignored):

      sh> cat test.cmds
      # colormap
      cmap heat
      # scale
      scale log
      # regions using image coords
      region circle {"x":588, "y":590, "radius":30, "tags":"source"}
      region circle {"x":390, "y":430, "radius":50, "tags":"background"}
      sh> cat test.cmds | js9 -

DS9/Funtools region syntax can also be used for regions:

      sh> cat test2.cmds
      cmap heat
      scale log
      wcssys fk5
      region box(23:23:35.236,+58:50:00.95,39.352",20.1679",24.0163)
      region ellipse(23:23:33.323,+58:47:41.50,29.6394",11.0139",25.7599)
      region polygon(23:23:19.379,+58:49:30.02,23:23:17.270,+58:49:40.93,23:23:14.834,+58:49:38.59,23:23:17.974,+58:49:13.64) {"tags": "background"}
      sh> cat test2.cmds | js9 -

The -p or --pipe switch also allows you to specify a string on the JS9 command line that will prefix all lines read from stdin. This allows you to send a regions file to JS9:

      sh> cat ds9.reg
      # Region file format: DS9 version 4.1
      global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1
      fk5
      box(23:23:35.236,+58:50:00.95,39.352",20.1679",24.0163)
      ellipse(23:23:33.323,+58:47:41.50,29.6394",11.0139",25.7599)
      polygon(23:23:19.379,+58:49:30.02,23:23:17.270,+58:49:40.93,23:23:14.834,+58:49:38.59,23:23:17.974,+58:49:13.64) # background
      sh> cat ds9.reg | js9 - region

Without the - ("dash") argument, standard input will not be read and the "region" command will be executed to return current regions.

Ordinarily, the js9 script talks to a displayed JS9 web page. It also can be used to start the JS9 Desktop app and load an image into the app's web page. The JS9 Desktop app simply requires that you install Electron.js, which is available here:

      https://www.electronjs.org/

Once this is done, use the -a switch to specify the app startup and the -w or --webpage switch to specify a web page (default is a nice, basic JS9 web page).

      sh> js9 -a ~/data/casa.fits

will start the JS9 Desktop app with a basic JS9 web page and load the Cas-A FITS files into the page, while:

      sh> js9 -a --webpage ~/js9/js9basics.html ~/data/casa.fits

will display the same image in one of the JS9 demo pages. For more details, see: Desktop JS9.

The js9 script also can be used to start up a new browser, display a JS9 web page, and then load an image into that page. To do this, use the -b or --browser switch to specify the browser (chrome, safari, or firefox) and the -w or --webpage switch to specify a web page (default is a basic JS9 web page). For example:

      sh> js9 -b chrome ~/data/casa.fits

will start a Chrome browser with a basic JS9 web page and load the Cas-A FITS files into the page, while:

      sh> js9 -b firefox -w ~/js9/js9basics.html ~/data/casa.fits

will use Firefox to display the same image in one of the JS9 demo pages.

Instead of supplying the browser name each time on the command line, you can set the environment variable JS9_BROWSER and just use the -b switch with no argument. (Note that you still must supply the -b switch, which tells the script to start a browser.) Similarly, the default web page can be configured using the JS9_WEBPAGE environment variable:

      sh> export JS9_BROWSER=chrome
      sh> export JS9_WEBPAGE=$HOME/js9/js9basics.html
      sh> js9 -b ~/data/casa.fits

Care must be taken that no JS9 web page is already being displayed when the -b or --browser switch is utilized, or else the specified image will be loaded into the existing web page (and no new browser will be started). Also, it is important to note that the Google Chrome browser must be started by the js9 script or it must be started by you using the --allow-file-access-from-files switch. Without this switch, Chrome will not permit a local HTML file to read other files. Finally, Mac OSX Safari occasionally experiences delayed data transfers when the js9 messaging script is located in a different Space (desktop) from the browser window, so keep these in the same Space.

If you do not have access to the js9 script, you can still send messages to a JS9 web page using wget or curl. This is possible because the JS9 helper also listens for HTTP connections. The syntax for HTTP-based commands is analogous to the scripting syntax, if somewhat more primitive: you pass a JSON-formatted string to the helper in a GET or POST request, as show below.

      export MYHOST="https://js9.si.edu:443"
      export ID="JS9"
      export WARGS="-q -O-"
      # GET request, using public api:
      wget $WARGS $MYHOST'/msg?{"id": "'$ID'", "cmd": "GetColormap"}'
      wget $WARGS $MYHOST'/msg?{"id": "'$ID'", "cmd": "SetColormap", "args": ["red"]}'
      wget $WARGS $MYHOST'/msg?{"id": "'$ID'", "cmd": "RunAnalysis", "args": ["counts"]}'
      # GET request, using command line api:
      wget $WARGS $MYHOST'/msg?{"id": "'$ID'", "cmd": "zoom"}'
      wget $WARGS $MYHOST'/msg?{"id": "'$ID'", "cmd": "zoom", "args": [2]}'
      wget $WARGS $MYHOST'/msg?{"id": "'$ID'", "cmd": "analysis", "args": ["counts"]}'
      # POST request, using public api:
      wget $WARGS --post-data='{"id": "'$ID'", "cmd": "GetColormap"}' $MYHOST/msg
      wget $WARGS --post-data='{"id": "'$ID'", "cmd": "SetColormap", "args": ["red"]}' $MYHOST/msg
      wget $WARGS --post-data='{"id": "'$ID'", "cmd": "RunAnalysis", "args": ["counts"]}' $MYHOST/msg

##### Last updated: September 2, 2021