{"id":13760363,"url":"https://github.com/jsa-aerial/hanami","last_synced_at":"2025-05-15T01:07:25.358Z","repository":{"id":45176749,"uuid":"103580757","full_name":"jsa-aerial/hanami","owner":"jsa-aerial","description":"Interactive arts and charts plotting with Clojure(Script) and Vega-lite / Vega. Flower viewing 花見 (hanami)","archived":false,"fork":false,"pushed_at":"2024-10-21T18:20:57.000Z","size":2986,"stargazers_count":400,"open_issues_count":8,"forks_count":12,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-14T03:21:44.287Z","etag":null,"topics":["charting-library","clojure","clojurescript","dataviz","plotting","reagent","recom","vega","vega-lite"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsa-aerial.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-14T20:53:49.000Z","updated_at":"2025-04-21T18:58:12.000Z","dependencies_parsed_at":"2024-01-06T12:19:54.449Z","dependency_job_id":"1f354215-e2d0-4a5a-b1db-c047a8d6fc27","html_url":"https://github.com/jsa-aerial/hanami","commit_stats":{"total_commits":417,"total_committers":4,"mean_commits":104.25,"dds":"0.021582733812949617","last_synced_commit":"e9cffd8e8062f66c98558ebbd64544d3836f49ab"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa-aerial%2Fhanami","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa-aerial%2Fhanami/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa-aerial%2Fhanami/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsa-aerial%2Fhanami/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsa-aerial","download_url":"https://codeload.github.com/jsa-aerial/hanami/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254103876,"owners_count":22015361,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["charting-library","clojure","clojurescript","dataviz","plotting","reagent","recom","vega","vega-lite"],"created_at":"2024-08-03T13:01:08.696Z","updated_at":"2025-05-15T01:07:20.349Z","avatar_url":"https://github.com/jsa-aerial.png","language":"Clojure","readme":"[![Clojars Project](https://img.shields.io/clojars/v/aerial.hanami.svg)](https://clojars.org/aerial.hanami)\n\n# Hanami\n\nInteractive arts and charts visualizations with Clojure(Script), Vega-lite, and Vega. Flower viewing 花見 (hanami)\n\n\u003ca href=\"https://jsa-aerial.github.io/aerial.hanami/index.html\"\u003e\u003cimg src=\"https://github.com/jsa-aerial/hanami/blob/master/resources/public/Himeji_sakura.jpg\" align=\"left\" hspace=\"10\" vspace=\"6\" alt=\"hanami logo\" width=\"150px\"\u003e\u003c/a\u003e\n\n**Hanami** is a Clojure(Script) library and framework for creating interactive visualization applications based in [Vega-Lite](https://vega.github.io/vega-lite/) (VGL) and/or [Vega](https://vega.github.io/vega/) (VG) specifications. These specifications are declarative and completely specified by _data_ (JSON maps). VGL compiles into the lower level grammar of VG which in turn compiles to a runtime format utilizting lower level runtime environments such as [D3](https://d3js.org/), HTML5 Canvas, and [WebGL](https://github.com/vega/vega-webgl-renderer). In addition to VGL and VG, Hanami is built on top of [Reagent](http://reagent-project.github.io/) and [Re-Com](https://github.com/Day8/re-com).\n\n\nTable of Contents\n=================\n\n   * [Overview](#overview)\n   * [Installation](#installation)\n   * [Features](#features)\n   * [Examples](#examples)\n      * [Simple cars](#simple-cars)\n      * [Instrumented barchart](#instrumented-barchart)\n      * [Contour plot using Vega template](#contour-plot-using-vega-template)\n      * [Tree Layout using Vega template](#tree-layout-using-vega-template)\n   * [Templates, Substitution Keys and Transformations](#templates-substitution-keys-and-transformations)\n      * [Function values for substitution keys](#function-values-for-substitution-keys)\n      * [Subtitution Key Functions](#subtitution-key-functions)\n      * [Template local defaults](#template-local-defaults)\n      * [Basic transformation rules](#basic-transformation-rules)\n      * [Meta data and the :USERDATA key](#meta-data-and-the-userdata-key)\n      * [Example predefined templates](#example-predefined-templates)\n      * [Example predefined substitution keys](#example-predefined-substitution-keys)\n      * [Data Sources](#data-sources)\n         * [File data source](#file-data-source)\n      * [Walk through example of transformation](#walk-through-example-of-transformation)\n   * [Application Construction](#application-construction)\n      * [Library overview](#library-overview)\n      * [Framework overview](#framework-overview)\n         * [framework topology graphic](#framework-topology-graphic)\n      * [Resource requirements](#resource-requirements)\n      * [Header](#header)\n      * [Tabs](#tabs)\n         * [Basics](#basics)\n         * [Configuration and behavior](#configuration-and-behavior)\n         * [Extension tabs](#extension-tabs)\n         * [Incremental bodies](#incremental-bodies)\n         * [Wrapping functions](#wrapping-functions)\n      * [Sessions](#sessions)\n      * [Messages](#messages)\n         * [Connection](#connection)\n         * [Session group](#session-group)\n         * [Tab updates](#tab-updates)\n         * [User messages](#user-messages)\n      * [Picture Frames](#picture-frames)\n         * [Empty Frames](#empty-frames)\n      * [Data Streaming](#data-streaming)\n      * [Client Only Apps](#client-only-apps)\n         * [Basic client app requirements](#basic-client-app-requirements)\n            * [Require resources](#require-resources)\n            * [Application initialization](#application-initialization)\n            * [Landing page - index.html](#landing-page---indexhtml)\n            * [Application start](#application-start)\n         * [Example Client Only Application](#example-client-only-application)\n      * [API](#api)\n         * [Templates and Substitution keys](#templates-and-substitution-keys)\n         * [Startup](#startup)\n            * [Server start](#server-start)\n            * [Client start](#client-start)\n         * [Message system](#message-system)\n            * [send msg](#send-msg)\n            * [user msg](#user-msg)\n         * [Client core](#client-core)\n            * [Visualization](#visualization)\n            * [Tab system](#tab-system)\n            * [Hanami main](#hanami-main)\n         * [Server core](#server-core)\n   * [Example Transform 'Gallery'](#example-transform-gallery)\n      * [Observed vs Binomial Model](#observed-vs-binomial-model)\n      * [Lowess smoothing of Tn-Seq fitness data](#lowess-smoothing-of-tn-seq-fitness-data)\n         * [With Overview   Detail](#with-overview--detail)\n      * [Interactive Cross plot Differential Gene Expression](#interactive-cross-plot-differential-gene-expression)\n\n[toc](https://github.com/ekalinin/github-markdown-toc)\n\n# Overview\n\n**Hanami** is a Clojure(Script) library and framework for creating interactive visualization applications based in [Vega-Lite](https://vega.github.io/vega-lite/) (VGL) and/or [Vega](https://vega.github.io/vega/) (VG) specifications. These specifications are declarative and completely specified by _data_ (JSON maps). VGL compiles into the lower level grammar of VG which in turn compiles to a runtime format utilizting lower level runtime environments such as [D3](https://d3js.org/), HTML5 Canvas, and [WebGL](https://github.com/vega/vega-webgl-renderer).\n\nIn keeping with the central data oriented tenet, Hanami eschews the typical API approach for generating specifications in favor of using recursive transforms of parameterized templates. This is also in keeping with the data transformation focus in functional programming, which is espcially nice in Clojure(Script).\n\nAn important aspect of this approach is that parameterized templates can be used to build other such templates by being higher level substitutions. In addition templates can be composed and this is an important idiomatic use. Furthermore, templates may be merged, though typically this is after transformation. The overall result enables the construction of sharable libraries of templates providing reusable plots, charts, and entire visualizations. Generally these will be domain and/or task specific. Hanami itself provides only a small set of very generic templates, which have proven useful in constructing more domain/task specific end results.\n\nHence, templates are a means to abstract all manner of visualization aspects and requirements. In this sense they are similar to what [Altair](https://altair-viz.github.io/) provides but without the complications and limitations of an OO class/method based approach.\n\n\n\n\n# Installation\n\nTo install, add the following to your project `:dependencies`:\n\n    [aerial.hanami \"0.15.1\"]\n\n\n\n\n# Features\n\n* Parameterized templates with recursive transformations\n  * Takes the place of the typical static procedural/functional API\n  * Purely data driven - no objects, classes, inheritance, whatever\n  * Completely open ended - users may define their own with their own defaults\n  * More general in scope than an API while capable of arbitrary specific detail\n* A tabbing system for named visulization groupings\n  * Multiple simultaneous independent and dependent visulizations per grouping\n  * Automatic grid layout\n  * Option system for customization\n* Enables specific application construction\n  * Application level page header instrumentation (re-com enabled)\n  * Application level external instrumentation of charts (re-com enabled)\n  * Full hiccup/re-com \"picture framing\" capability for independent charts\n  * Multiple simultaneous (named) applications\n  * Multiple sessions per application\n    * Shared named sessions\n  * Application extensible messaging capability\n  * Data streaming capable\n    * Realtime chart/plot updates with data updates\n* Uses light weight websocket [messaging system](https://github.com/jsa-aerial/hanasu)\n\n\n\n\n# Examples\n\n```Clojure\n(ns hanami.examples\n  (:require [aerial.hanami.common :as hc]\n            [aerial.hanami.templates :as ht]\n            [aerial.hanami.core :as hmi]\n            ...)\n```\n\nIn all of the documentation, these namespaces are referred to by the shorthand provided in this require example.\n\n* `aerial.hanami.common` == `hc`\n* `aerial.hanami.templates` == `ht`\n* `aerial.hanami.core` == `hmi`\n\n\n## Simple cars\n\nAs a first example let's compare a Hanami template with corresponding Altair code. This is a the typical scatter plot example from the Vega-Lite developers used in many tutorials and by others in various places. First, the Altair:\n\n```Python\ncars = data.cars()\n\nalt.Chart(cars).mark_point().encode(\n    x='Horsepower',\n    y='Miles_per_Gallon',\n    color='Origin',\n    ).interactive()\n```\n\nHanami template version:\n\n```Clojure\n(hc/xform ht/point-chart\n  :UDATA \"data/cars.json\"\n  :X \"Horsepower\" :Y \"Miles_per_Gallon\" :COLOR \"Origin\")\n```\n\nWhich, using the standard default [substitution keys](#example-predefined-substitution-keys) transforms to this Vega-Lite JSON specification:\n\n```Clojure\n{:data {:url \"data/cars.json\"},\n :width 400,\n :height 300,\n :background \"floralwhite\",\n :encoding\n   {:x {:field \"Horsepower\", :type \"quantitative\"},\n    :y {:field \"Miles_per_Gallon\", :type \"quantitative\"},\n    :color {:field \"Origin\", :type \"nominal\"},\n    :tooltip\n    [{:field \"Horsepower\", :type \"quantitative\"}\n     {:field \"Miles_per_Gallon\", :type \"quantitative\"}]}}\n ```\n\n\nWhen rendered, both the Altair code and Hanami template, result in the following visualization, where the mouse is hovering over the point given by [132, 32.7]:\n\n![Hanami pic 1](resources/public/images/hanami-cars-1.png?raw=true)\n\n\n## Instrumented barchart\n\nHanami visualizations may be instrumented with external active componets (react/reagent/re-com) to enable external transforms on them. An example of an instrumented chart:\n\n```Clojure\n(hc/xform ht/bar-chart\n  :USERDATA\n  (merge\n   (hc/get-default :USERDATA)\n   {:vid :bc1\n    :slider `[[gap :size \"10px\"] [label :label \"Add Bar\"]\n              [label :label ~minstr]\n              [slider\n               :model :m1\n               :min ~min, :max ~max, :step 1.0\n               :width \"200px\"\n               :on-change :oc1]\n              [label :label ~maxstr]\n              [input-text\n               :model :m1\n               :width \"60px\", :height \"26px\"\n               :on-change :oc2]]})\n  :HEIGHT 300, :WIDTH 350\n  :X \"a\" :XTYPE \"ordinal\" :XTITLE \"Foo\" :Y \"b\" :YTITLE \"Bar\"\n  :DATA data)\n```\n\nWhich requires the active component implementing the instrument to be coded over on the client side. For this simple example, the test code on the client is a branch inside of a `cond` testing for the `:slider` key and value and then processing it:\n\n```Clojure\n      (let [cljspec spec\n            udata (cljspec :usermeta)\n            default-frame {:top [], :bottom [],\n                           :left [[box :size \"0px\" :child \"\"]],\n                           :right [[box :size \"0px\" :child \"\"]]}]\n        (cond\n          ...\n          (udata :slider)\n          (let [sval (rgt/atom \"0.0\")]\n            (printchan :SLIDER-INSTRUMENTOR)\n            (merge default-frame\n                   {:top (xform-recom\n                          (udata :slider)\n                          :m1 sval\n                          :oc1 #(do (bar-slider-fn tabid %)\n                                    (reset! sval (str %)))\n                          :oc2 #(do (bar-slider-fn tabid (js/parseFloat %))\n                                    (reset! sval %)))}))\n```\n\nWhen this chart is rendered, it shows as (left, before slider move; right, after slider move)\n\n![Hanami pic 2](resources/public/images/instrumented-chart-1a.png?raw=true)\n![Hanami pic 3](resources/public/images/instrumented-chart-1b.png?raw=true)\n\n\n## Contour plot using Vega template\n\nAn example using a Vega template for contour plotting. An important point to recognize here is that Vega specifications are also pure data, so the exact same recursive transformation works on Vega templates as Vega Lite templates:\n\n```Clojure\n(hc/xform\n  ht/contour-plot\n  :MODE \"vega\"\n  :HEIGHT 400, :WIDTH 500\n  :X \"Horsepower\", :XTITLE \"Engine Horsepower\"\n  :Y \"Miles_per_Gallon\" :YTITLE \"Miles/Gallon\"\n  :UDATA \"data/cars.json\"\n  :XFORM-EXPR #(let [d1 (% :X)\n                     d2 (% :Y)]\n                 (format \"datum['%s'] != null \u0026\u0026 datum['%s'] !=null\" d1 d2)))\n```\n\nThis generates far too much to show here, as Vega is a much lower level formal specification language than Vega Lite. This renders as:\n\n![Hanami pic 3.1](resources/public/images/contour-1.png?raw=true)\n\n\n## Tree Layout using Vega template\n\nAnother interesting Vega example uses the `tree-layout` template. Using this template for such layouts abstracts away a good deal of low level complexity. In this example a software system module dependency graph is rendered.\n\n```Clojure\n(hc/xform\n ht/tree-layout\n  :MODE \"vega\"\n  :WIDTH 650, :HEIGHT 1600\n  :UDATA \"data/flare.json\"\n  :LINKSHAPE \"line\" :LAYOUT \"cluster\"\n  :CFIELD \"depth\")\n ```\n\n![Hanami pic 3.2](resources/public/images/tree-layout-1.png?raw=true)\n\n\n\n\nA number of other examples appear at the end of this README, along with their transformations and renderings.\n\n\n\n\n# Templates, Substitution Keys and Transformations\n\n_Templates_ are simply maps parameterized by _substitution keys_. Generally, templates will typically correspond to a legal VG or VGL specification or a legal subcomponent thereof. For example, a complete VGL specification (rendered as Clojure) is a legal template - even though it has no substitution keys. At the other extreme, templates can correspond to pieces of specifications or subcomponents. These will always have substitution keys - if they didn't there would be no point to them.\n\n_Substitution Keys_ can be considered or thought of in two ways. They are the keys in the substitution map(s) of the recursive transformer [hc/xform](#templates-and-substitution-keys). There is a default map `hc/_defaults` (which is an atom containing the map) with various default substitution keys and their corresponding default values. This map can be updated and `xform` can also be supplied a variadic list of substitution keys and their values for a given invocation.\n\nThe second way of thinking about substitution keys is that they are the starting values of keys in templates. So, they represent parameterized values of keys in templates.\n\nAs an example consider the following. In a template we have a field and value `:field :X`. The `:X` here is a substitution key - it will be replaced as the value of `:field` during transformation by _its_ value in the current substitution map. In the default substitution map `hc/_defaults` `:X` has a value of \"x\", so the result will be `:field \"x\"`.\n\nA more complex example would be the field and value `:encoding :ENCODING` in the predefined subcomponent `ht/view-base` which lays out the structure of a Vega-Lite _view_.  As before `:ENCODING` will be replaced during transformation by the value of `:ENCODING` in the substitution map. However, the default value of `:ENCODING` is the predefined subcomponent `ht/xy-encoding`. This value is a map describing the structure of a view's `x-y` encodings and contains many fields with their own substitution keys. So, to produce a final value, `ht/xy-encoding` is recursively transformed so that the final value of `:encoding` is a fully realized `x-y` encoding for the view being processed. `ht/view-base` has several other such fields and is itself recursively transformed in the context of the current substitution map. And its final value will be the base of a chart, such as a line chart (`ht/line-chart`) or area chart (`ht/area-chart`), or some new plot/chart/layout/etc of your own for some domain specific application.\n\n\n## Function values for substitution keys\n\nA substitution key may have a function of one argument for its value: `(fn[submap] ...)`. The `submap` parameter is the current substitution map in the transformation. This map contains the special key `::hc/spec` whose value is the initial (input) specification as well as all current substitution keys and their current values. This is useful in cases where a substitution key's value should be computed from other values. For example, a typical ([Saite](https://github.com/jsa-aerial/saite#user-tabs) does this by default) use case would be to compute the _label_ (display name) of a tab from its _id_:\n\n```Clojure\n        :TLBL #(-\u003e :TID % name cljstr/capitalize)\n```\n\nThis takes the tab's id, which here is a keyword, converts to a string and returns the capitalized version.\n\n\n## Subtitution Key Functions\n\nA more general version of the function as value of a substitution key is provided by the `hc/subkeyfns` map and associated processing. The function [hc/update-subkeyfns](#templates-and-substitution-keys) can be used to register a function for a subtitution key. This is a 'global' function - separate from any value given for the key. These functions take three arguments: `(fn[submap, subkey, subval] ...)`.\n\nWhere the `submap` parameter is the current substitution map in the transformation; the `subkey` parameter is the substitution key for this function; and the `subval` parameter is the _value_ of the substitution key.\n\nThese functions can be very useful in providing a further level of abstractions is template specifications. For example, it is very typical that a `color` specification is intended for the mark of a chart (line-chart, bar-chart, point-chart, etc). providing a simple string indicating the data element to facet with colors makes for a cleaner abstraction, but this needs to be converted to correct full form `field` and `type` element. Having a subkeyfn for the `hc/color-key` (default `:COLOR`) supports this sort of processing:\n\n```Clojure\n        color-key\n         (fn[xkv subkey subval]\n           (if (string? subval)\n             (xform ht/default-mark-props (assoc xkv :MPFIELD subval))\n             subval))\n```\n\nThere is a default set of such keys provided with this particular case being one of them. There are a few others, including one which implements the [file](#file-data-source) data source capability for specifications.\n\n\n## Template local defaults\n\nAs of version `0.12.9` there is support for default parameterization per template (where 'template' here means any map whatsoever).  A new transformation control key is introduced, `:aerial.hanami.templates/defaults` which can be used to  supply a map, local to the template, containing default values of substitution keys.  The substitution keys may be those specific only to the template or any other enclosing template, or for changing global defaults.  The values are in effect for the duration of the template's processing, unless a more nested template has its own local defaults which override some of the outer's values.  In all cases, the in line (point of call) user specified values to [hc/xform](#templates-and-substitution-keys) take precedent over any template local defaults.\n\nLocal defaults can be a nice way to deliver new templates without any need to setup new substitution keys in the global registry or require users to add many key-value pairs to their call to [hc/xform](#templates-and-substitution-keys) using the new templates.  Addtionally they can automatically parameterize the underlying templates (those used in constructing a new template) to reflect the value of them required for the form of a new template.  As an example, let's look at the following, which will show the construction of a new `trend-chart` for plotting base data along with a smoothed trend line for it.\n\n```Clojure\n;;; Base layer for trend-chart\n(def trend-layer\n  (assoc ht/line-chart\n         :aerial.hanami.templates/defaults\n         {:X :data/x\u003e :XTYPE :xtype\u003e\n          :Y :data/y\u003e :YTYPE :ytype\u003e\n          :YSCALE {:zero false}\n          :DATA hc/RMV\n          :WIDTH :width\u003e :HEIGHT :height\u003e\n          :USERDATA hc/RMV}))\n\n```\n\nThis gives the base form for what the two layers will look like.  It is simply the base `ht/line-chart` parameterized to give the default value of each layer when transformed.  The new trend chart will use `:data/x\u003e` and `:data/y\u003e` as the parameters for x and y data fields, along with corresponding parameters for the types of the fields `:xtype\u003e` and `:ytype\u003e`.  Next we use this base layer template to create the `trend-chart` template:\n\n```Clojure\n;;; Trend chart - two layers.  1. base data, 2. loess trend line\n;;; Parameters :data/x for x-axis data. :data/y\u003e for y-axis data\n;;;            :xtype\u003e and :ytype\u003e for axis types, will default to :XTYPE :YTYPE\n;;;            :width\u003e, will default to 700\n;;;            :height\u003e, will default to :HEIGHT default\n;;;            :trend-color\u003e for loess line, defaults to \"firebrick\"\n(def trend-chart\n  (assoc ht/layer-chart\n         :description \"A two layer plot of base data and its smoothed trend line given by loess transform\"\n         :aerial.hanami.templates/defaults\n         {:LAYER [(hc/xform trend-layer)\n                  (hc/xform\n                   trend-layer\n                   :TRANSFORM [{:loess :data/y\u003e :on :data/x\u003e}]\n                   :MCOLOR :trend-color\u003e)]\n          :trend-color\u003e \"firebrick\"\n          :xtype\u003e :XTYPE :ytype\u003e :YTYPE\n          :width\u003e 700\n          :height\u003e (hc/get-defaults :HEIGHT)}))\n```\n\nHere we use the underlying templates of the predefined `ht/layer-chart` and the new `trend-layer`.  The layer structure is given by the default for `:LAYER` as being the two transformed cases for `trend-layer`. The first layer transform just ensures the result has all the new parameters in place.  The second also gives the loess transform and provides for a new `trend-color` for custom coloring the trend line, with a default of \"firebrick\".  The width of the chart gets a new default of 700 while the height defaults to the global `:HEIGHT` default.  The new `:xtype\u003e` and `:ytype\u003e` axis types get defaults of whatever the global `:XTYPE` and `:YTYPE` values are.\n\nThis can now be used in a highly simplified form such as the following:\n\n```Clojure\n(let [[cols data] (get-trend-data file)]\n (hc/xform\n  trend-chart\n  :DATA (mapv #(zipmap cols %) data)\n  :data/x\u003e :year :xtype\u003e :temporal\n  :data/y\u003e :C))\n\n```\n\nResulting in this output:\n\n![trend-chart](resources/public/images/trend-chart.png?raw=true)\n\n\n\n## Basic transformation rules\n\nThere are some important rules that guide certain aspects of the recursive transformation process. These aspects are really only important in advanced template contruction, but if you find yourself in such circumstances, they are quite useful and helpful.\n\n* Defaults for substitution keys are always overridden by values given for them in a call to [hc/xform](#templates-and-substitution-keys).\n\n* Any field whose final value is the special `hc/RMV` value, will be removed from the corresponding specification or subcomponent thereof.\n\n* Any field whose final value is an empty collection, will have this value replaced by `hc/RMV` and thus removed from its containing collection and specification. This is a very important transformation rule as it enables highly general substitution values. See for example such values as `ht/data-options`.\n\n* While the simplest way to look at the transformation process is as a continuous transform until the last output is the same as the input, the actual processing is a preorder depth first walk and replacement. Typically, this implementation detail should not factor in to any template authoring, as they should be declarative in nature. However, there may be cases where this ordering can help in constructing proper substitution keys and values.\n\n\n## Meta data and the `:USERDATA` key\n\nVega and Vega-Lite support the inclusion of application specific meta data in specifications via the `:usermeta` top level key in a specification. To Vega and Vega-Lite, this key is completely transparent and is not used in any way by their processing. This affords applications using them to include application specific data, in particular, _control_ data.\n\nHanami defaults the `:usermeta` field to the substitution key `:USERDATA`, which has a default value of `RMV`. So, by default, there is no application meta data. However, several aspects of Hanami's [messaging system](#messages), [session management](#sessions), [picture frames](#picture-frames) and [tab](#tabs) system expect and make use of control data supplied via the `:usermeta` field and associated values for the `:USERDATA` substitution key. So, if you plan on using any of that, you will need to supply a value for this key - either as a default (via `hc/update-defaults`) or explicitly in a transform (`hc/xform`). Of course, if you have your own application specific meta/control data that needs to be supplied to clients, you can provide your own fields and values here.\n\nHanami understands the following 'special' fields:\n\n* `:tab` - value is a map of fields (`:id`, `:label`, `:opts`) identifying and controlling tabs.\n  * `:id` - value is an id for the tab. Typically a keyword\n  * `:label` - value is the display name for the tab\n  * `:opts` - value is a map of fields (`:order`, `:eltsper`, `:size`, and :wrapfn)\n    * `:order` - value is either `:row` or `:col` for either row based grid layouts or column based grid layouts\n    * `:eltsper` - value is an integer for the maximum number of row or col entries. For example a value of 2 would mean 2XN grids for rows and Nx2 grids for columns\n    * `:size` - value is a flexbox size indicator. Best known values are \"auto\" for row based grids and \"none\" for column based grids\n    * `:wrapfn` - value is a function which takes the hiccup element for the tab's body (fully structured grid layout) and must return a hiccup/re-com element. The intent is a user may wish to encase the grid body in extra ancillary support layout / active elements. See [wrapping functions](#wrapping-functions)\n\n* `:opts` - value is a map of fields controlling Vega/Vega-Lite options. This is **not** the `[:tab :opts]` path and value!\n  * `:export` - value is a map of boolean value fields (`:png`, `:svg`). Setting these true will provide implicit saving options in the popup Vega-Embed options button (typically upper right circle with \"...\" content).\n  * `:scaleFactor` - value is a number indicating how much to scale image when exporting png or svg.\n  * `:renderer` - value is either \"canvas\" or \"svg\"\n  * `:mode` - value is either \"vega-lite\" or \"vega\". Indicates whether the specification is a Vega spec or a Vega-Lite spec (that will be compiled to Vega before rendering). Hanami default values are in (`hc/default-opts` :vgl) which defaults to\n```Clojure\n    {:export {:png true, :svg true}\n     :scaleFactor :SCALEFACTOR\n     :editor true\n     :source false\n     :renderer :RENDERER ; either \"canvas\" or \"svg\" - see defaults\n     :mode :MODE}        ; either \"vega-lite\" or \"vega\" - see defaults\n```\n\n* `:vid` - value is an application specific identifier of the associated visualization (if any). This is used as the HTML `id` tag for the element directly containing the visualization. Useful when needing to dynamically manipulate the container and/or the visualization.\n\n* `:msgop` - value is one of `:register`, `:tabs`, or some application specific operator. These messages are from the server to the client. `:register` is sent on client connection for [registration](#connection) purposes. `:tabs` is used to [update tabs](#tab-updates) and their content, if tabs are used. [User messages](#user-messages) have application specific operators and are sent when your application deems they should be sent.\n\n* `:session-name` - the name of session(s) which will receive the complete specification (either via a `:tabs` message or some user message).\n\nAs an example, [Saite](https://github.com/jsa-aerial/saite) has an init function as part of its start up which sets the values of `:USERDATA` and subsequent defining substitution keys as:\n\n\n```Clojure\n:USERDATA\n{:tab {:id :TID, :label :TLBL, :opts :TOPTS},\n :frame {:fid :FID, :top :TOP, :bottom :BOTTOM, :left :LEFT, :right :RIGHT}\n :opts :OPTS,\n :vid :VID,\n :msgop :MSGOP,\n :session-name :SESSION-NAME}\n\n:OPTS (hc/default-opts :vgl)\n:SESSION-NAME \"Exploring\"\n:TID :expl1\n:TLBL #(-\u003e :TID % name cljstr/capitalize)\n:TOPTS (hc/default-opts :tab)\n:MSGOP :tabs\n```\n\nWhere `hc/default-opts` is:\n\n```Clojure\n{:vgl {:export {:png true, :svg true}\n       :scaleFactor :SCALEFACTOR\n       :editor true\n       :source false\n       :renderer :RENDERER ; either \"canvas\" or \"svg\" - see defaults\n       :mode :MODE}        ; either \"vega-lite\" or \"vega\" - see defaults\n :tab {:order :ORDER       ; either :row or :col - see defaults\n       :eltsper :ELTSPER   ; count of elements per row/col - see defaults\n       :size \"auto\"}}\n```\n\n\n## Example predefined templates\n\nHere are some examples as provided by the name space `aerial.hanami.templates` which is always referred to in documentation as `ht/`.\n\nA number of 'fragments':\n\n```Clojure\n(def default-tooltip\n  [{:field :X :type :XTYPE}\n   {:field :Y :type :YTYPE}])\n\n(def default-mark-props\n  {:field :MPFIELD :type :MPTYPE})\n\n(def default-row\n  {:field :ROW :type :ROWTYPE})\n\n(def data-options\n  {:values :VALDATA, :url :UDATA, :name :NDATA})\n\n(def interval-scales\n  {:INAME\n   {:type \"interval\",\n    :bind \"scales\", ; \u003c-- This gives zoom and pan\n    :translate\n    \"[mousedown[event.shiftKey], window:mouseup] \u003e window:mousemove!\"\n    :encodings :ENCODINGS,\n    :zoom \"wheel!\",\n    :resolve :IRESOLVE}})\n\n(def mark-base\n  {:type :MARK, :point :POINT,\n   :size :MSIZE, :color :MCOLOR,\n   :filled :MFILLED})\n```\n\nA couple 'subcomponents':\n\n```Clojure\n(def xy-encoding\n  {:x {:field :X\n       :type :XTYPE\n       :timeUnit :XUNIT\n       :axis :XAXIS\n       :scale :XSCALE\n       :aggregate :XAGG}\n   :y {:field :Y\n       :type :YTYPE\n       :timeUnit :YUNIT\n       :axis :YAXIS\n       :scale :YSCALE\n       :aggregate :YAGG}\n   :opacity :OPACITY\n   :row :ROWDEF\n   :column :COLDEF\n   :color :COLOR\n   :size :SIZE\n   :shape :SHAPE\n   :tooltip :TOOLTIP})\n\n(def view-base\n  {:usermeta :USERDATA\n   :title :TITLE\n   :height :HEIGHT\n   :width :WIDTH\n   :background :BACKGROUND\n   :selection :SELECTION\n   :data data-options\n   :transform :TRANSFORM\n   :encoding :ENCODING})\n```\n\nAnd some charts.\n\n```Clojure\n;; Useful for empty picture frames\n(def empty-chart\n  {:usermeta :USERDATA})\n\n(def line-chart\n  (assoc view-base\n         :mark (merge mark-base {:type \"line\"})))\n\n(def point-chart\n  (assoc view-base\n         :mark (merge mark-base {:type \"circle\"})))\n\n(def grouped-bar-chart\n  {:usermeta :USERDATA\n   :title  :TITLE\n   :height :HEIGHT\n   :width  :WIDTH\n   :background :BACKGROUND\n   :selection :SELECTION\n   :data data-options\n\n   :mark \"bar\"\n   :encoding :ENCODING\n\n   :config {:bar {:binSpacing 0\n                  :discreteBandSize 1\n                  :continuousBandSize 1}\n            :view {:stroke \"transparent\"},\n            :axis {:domainWidth 1}}})\n```\n\n## Example predefined substitution keys\n\nAll of these are taken from `hc/_defaults` They are chosen so as to indicate how some aspects of the above template examples get transformed.\n\n```Clojure\n         :USERDATA RMV, :MODE \"vega-lite\", :RENDERER \"canvas\", :SCALEFACTOR 1\n         :TOP RMV, :BOTTOM RMV, :LEFT RMV, :RIGHT RMV\n         :BACKGROUND \"floralwhite\"\n         :OPACITY RMV\n\n         ;; Note that removed things will get Vega/Vega-Lite defaults\n         :TITLE RMV, :TOFFSET RMV\n         :HEIGHT 300, :WIDTH 400, :DHEIGHT 60\n\n         ;; get-data-vals is a function which handles :DATA and :FDATA\n         :VALDATA get-data-vals\n         :DATA RMV, :FDATA RMV, :SDATA RMV, :UDATA RMV, :NDATA RMV\n\n         ;; Describes the x encoding in xy-encoding. Similar for y encoding\n         :X \"x\", :XTYPE, \"quantitative\", :XUNIT RMV\n         :XSCALE RMV, :XAXIS {:title :XTITLE, :grid :XGRID, :format :XFORMAT}\n         :XTITLE RMV, :XGRID RMV, :XFORMAT RMV\n\n         ;; Aggregation transforms\n         :AGG RMV, :XAGG RMV, :YAGG RMV\n\n         ;; Mark properties\n         :POINT RMV, :MSIZE RMV, :MCOLOR RMV, :MFILLED RMV\n         :MPTYPE \"nominal\", :SHAPE RMV, :SIZE RMV\n\n\n         ;; Note that by default then :ROWDEF -\u003e {:field :ROW :type :ROWTYPE} -\u003e\n         ;; {:field RMV, :type RMV} -\u003e {} -\u003e RMV. See transformation rules\n         ;; Similar for :COLDEF\n         :ROWDEF ht/default-row :ROW RMV, :ROWTYPE RMV\n\n         :TOOLTIP ht/default-tooltip\n         :ENCODING ht/xy-encoding\n\n         :TRANSFORM RMV\n         :SELECTION RMV\n```\n\n\n## Data Sources\n\nVisualizations are based in and so require data to realize them. Specifications provide several means of declaring the source and processing of data to be used in a visualization. The underlying Vega and Vega-Lite systems provide for several of these by various means. Hanami currently directly supports three of these plus a fourth. The keys and expected values are given in this section.\n\n* `:DATA` - expects an explicit vector of maps, each map defining the data fields and values\n* `:UDATA` - expects a relative URL (for example \"data/cars.json\") or a fully qualified URL to a `csv` or `json` data file.\n* `:NDATA` - expects a named Vega _data channel_.\n\nThese three are based directly on the underlying Vega and Vega-Lite options. The fourth, file data sources, is provided by Hanami.\n\n### File data source\n\n`:FDATA \u003cfilepath | [filepath, type-vector-or-map]]`\n\nThe `filepath` is a full path for the OS and can denote a file with extensions `clj`, `edn`, `json`, or `csv`. These extensions indicate the file types - no other checking is done. For the `clj`, `edn`, and `json` the content must be a vector of maps, where each map is a record of data fields and their values. CSV files are converted to vectors of maps, each map built from the column names and a row's corresponding values.\n\nFor `csv`, a `type-vec-or-map` may be provided as the second value of a vector pair (tuple). This can be either a vector of type indicators in 1-1 correspondence with the column names of the csv file, or a map where each key is one of the column names and the value is a type indicator. Type indicators supported are (the strings): \"string\", \"int\", \"float\", and \"double\". In either case, the strings in a row associated with the type indicators are converted per the indicator. The default type indicator for the case of maps is \"string\", i.e., return the original string value.\n\nExample:\n\n```Clojure\n(hc/xform my-plot-template\n  :FDATA [\"/Data/RNAseq/Exp-xyz/Out/dge-xyz.csv\" {\"dge\" \"float\"}])\n```\n\nThis will create a vector of maps from the csv content, where the \"dge\" field in each map will have its value converted to a floating point number.\n\n\n## Walk through example of transformation\n\nIt's worth having a look at what happens with the simple [car chart](#simple-cars) template example and its transformations. The value of `ht/point-chart` is:\n\n```Clojure\n(def point-chart\n  (assoc view-base\n         :mark (merge mark-base {:type \"circle\"})))\n```\n\nWe have already seen ([above](#example-predefined-templates)) what `view-base`, `data-options`, and `mark-base` are, so we know that the starting value of `point-chart` is:\n\n```Clojure\n{:usermeta :USERDATA\n :title :TITLE\n :height :HEIGHT\n :width :WIDTH\n :background :BACKGROUND\n :mark {:type \"circle\"\n        :point :POINT\n        :size :MSIZE\n        :color :MCOLOR\n        :filled :MFILLED},\n :selection :SELECTION\n :data {:values :VALDATA, :url :UDATA, :name :NDATA}\n :transform :TRANSFORM\n :encoding :ENCODING}\n ```\n\nWe already have seen what the [default values](#example-predefined-substitution-keys) of the substitution keys here are. In the transform, explicit values are given for `:UDATA`, `:X`, `:Y`, and `:COLOR`. We will see about `:COLOR` a bit later, but with the values for the first three, conceptually, we have a \"first version\":\n\n```Clojure\n{:height 300\n :width 400\n :background \"floralwhite\"\n :mark {:type \"circle\"}\n :data {:url \"data/cars.json\"}\n :encoding {:x {:field \"Horsepower\"\n                :type \"quantitative\"\n                :axis {:title :XTITLE, :grid :XGRID, :format :XFORMAT}}\n            :y {:field \"Miles_per_Gallon\"\n                :type \"quantitative\"\n                :axis {:title :YTITLE, :grid :YGRID, :format :YFORMAT}}\n            :row {:field :ROW :type :ROWTYPE}\n            :column {:field :COLUMN :type :COLTYPE}\n            :color :COLOR\n            :tooltip [{:field \"Horsepower\" :type \"quantitative\"}\n                      {:field \"Miles_per_Gallon\" :type \"quantitative\"}]}}\n```\n\nBy the [second rule](#basic-transformation-rules) of transformation, we also know that the `:axis`, `:row`, and `:column` fields will be replaced first by `{}` (since all of their fields default to `RMV`) and then by the [third rule](#basic-transformation-rules) they will be removed. So, conceptually, the 'next' version is:\n\n```Clojure\n{:height 300, :width 400, :background \"floralwhite\"\n :mark {:type \"circle\"}\n :data {:url \"data/cars.json\"}\n :encoding {:x {:field \"Horsepower\" :type \"quantitative\"}\n            :y {:field \"Miles_per_Gallon\" :type \"quantitative\"}\n            :color :COLOR\n            :tooltip [{:field \"Horsepower\" :type \"quantitative\"}\n                      {:field \"Miles_per_Gallon\" :type \"quantitative\"}]}}\n```\n\nOK, `:COLOR` is all we have left. As shown in the section on [substitution key functions](#subtitution-key-functions) Hanami has a default such function for the `hc/color-key` (default `:COLOR`). If the `subval` of the key is a string, then this function returns the value `(xform ht/default-mark-props (assoc xkv :MPFIELD subval))`. As we know from above, the value of `ht/default-mark-props` is `{:field :MPFIELD :type :MPTYPE}` and so the function here returns `{:field \"Origin\" :type \"nominal\"}`. So, the final value of the transformation is:\n\n```Clojure\n{:height 300, :width 400, :background \"floralwhite\"\n :mark {:type \"circle\"}\n :data {:url \"data/cars.json\"}\n :encoding {:x {:field \"Horsepower\" :type \"quantitative\"}\n            :y {:field \"Miles_per_Gallon\" :type \"quantitative\"}\n            :color {:field \"Origin\" :type \"nominal\"}\n            :tooltip [{:field \"Horsepower\" :type \"quantitative\"}\n                      {:field \"Miles_per_Gallon\" :type \"quantitative\"}]}}\n```\n\nThe reason for caveat uses of 'conceptually' in the above description is the [final rule](#basic-transformation-rules) of transformation. Which says that the actual transformation process is via a depth first walk and replacement. So, for example, `:encoding` would have only had its full final value, not any intermediate value(s).\n\n\n\n\n# Application Construction\n\nHanami is an visualization application developement enabler. It is a library with optional framework aspects provided for both server side (Clojure) and client side (ClojureScript) development. The client is most typically expected to be in the browser, but technically may not be.\n\n## Library overview\n\nThe library portion of Hanami centers on\n\n* [Templates, Substitution Keys and Transformations](#templates-substitution-keys-and-transformations)\n* Simple, potent, _clean_ [messaging](#messages) system\n* Clean single point [Reagent](http://reagent-project.github.io/) (React lifecycle) component for compiling, _both_ Vega and Vega-Lite and rendering the result.\n\n\nThese bits are not opionated in how to go about developing a domain specific visualization application. There is no constraint on how page(s) are to be laid out, what sort of ancillary and support components should be used, what CSS is used, etc. You can use Hiccup or [Re-Com](https://github.com/Day8/re-com) or whatever to layout and structure your application. Nor is there any opinion about the structure of the server side. And while there are a set of defaults, there are no requirements or expectations about the form, makeup, or content of templates and substitution keys. You can replace, augment, or change any or all of the defaults.\n\n\n## Framework overview\n\nThe framework portion of Hanami _is_ opinionated, though not too stringently. It consists of\n\n* Client side application [header](#header) as provided by a user supplied function of zero arguments, which is expected to return a hiccup/re-com value which lays out the page header of the application. This value can simply be empty if you don't want this.\n\n* Named [session](#sessions) groups. The default (client) header function directly supports this by providing an input text box to specify which session group is desired. Any session in a session group of name _name_ will receive any messages sent to that group. This supports dynamic sharing.\n\n* A [tab system](#tabs) for automatically structuring both your application layout (each tab can be a page, chapter, subsection, etc) and the structure and content of each such component.\n\nThese combine to form the basic page layout from this 'framework perspective' as shown here:\n\n### framework topology graphic\n\n![Hanami framework layout](resources/public/images/framework-page-structure.png?raw=true)\n\nThe header area is constructed by the `:header-fn` argument of the [client start](#client-start) function. The tab bar is dynamically constructed via `:tabs` [messages](#tab-updates) or by explict calls to the [hmi/tabs](#tab-system) client function. The _content_ of each tab's body is also constructed dynamically via these same means. If the tab doesn't exist, it will be created and added to the tab bar at the time it's body is also rendered. Updates to a tab will simply update the existing tab's body.\n\nAs an example [Saite](https://github.com/jsa-aerial/saite) makes use of the framework aspects of Hanami and here is an example page layout from a session in it.\n\n![Hanami framework layout](resources/public/images/saite-framework-page.png?raw=true)\n\n\n## Resource requirements\n\nThe basic resource requirements are the various stylesheets, mostly in support of [re-com](https://github.com/Day8/re-com), that are shown in the development 'landing page' [index.html](https://github.com/jsa-aerial/hanami/blob/master/resources/public/Fig/index.html). When you create your own `index.html` landing page - or generate it dynamically - you will need to include these resources.\n\nAlso, as indicated in the development `index.html`, you will need to indicate where your final compiled JavaScript implementing your application will be and what it is called.\n\n**NOTE**: If you will be running [serverless](#client-only-apps), you will need to make sure that the resources and final javascript are located relative to where you load your landing page from or that all the links are absolute.\n\n\n## Header\n\nThe client side [start](#client-start) function has a `:header-fn` parameter which is a function of a single parameter that should return the main application header area. The default function for this returns a header which supports multiple named [session groups](#sessions). This is one mechanism where by you can insert global controls, logos, application 'avatars', etc. Alternatively you may choose to have this return nothing and wait for the [application initialization](#connection) message, to perform the application setup. The latter may be more appealing if you compute a number of fields that support the contruction of a more sophisticated header.\n\n\n## Tabs\n\nThe tab system of Hanami is intended to be a fairly general purpose document structuring and layout capability for a variety of visualization applications. But that very fact means it has some level of 'opinion' on such layouts and so you may not want to use it if you need total general / manual layouts. But for the very large subset of cases where it fits nicely it can be very helpful. There is a set of [functions and components](#tab-system) of the client API for dealing with the tab system. **If you use the standard framework main components, you will not have to use this API** - it is all handled for you. The API is given in case you need / want to set up your own components which make use of it.\n\n\n### Basics\n\nAs depicted in the [framework topology graphic](#framework-topology-graphic), the tab system has a dedicated area for the tab bar. Tabs are dynamically added to the tab bar from left to right. They may also be deleted. Any selected tab will have its body displayed in the dedicated content area. If you use Hanami's standard framework, the main components will drive all tab actions and updates.\n\n### Configuration and behavior\n\nTo use tabs, the `:usermeta` field of specifications must contain the `:tab` key whose value must be a map. The keys and values of this map are detailed in the tab section of the [USERDATA](#meta-data-and-the-userdata-key) section of this documentation. The `:id` field is the primary key for identifying and manipulating a tab and its body. The `:label` field is for human readable naming of the tab. The `:opts` field details the auto layout format for the tab's body. It is worth noting that if the `:eltsper` field has a value of 1, the effect is to have the body layed out linearly from top to bottom in the order the specifications were given to the tab.\n\nTabs are added and updated via the [update-tabs](#tab-system) client core function. This function takes a vector of specifications and groups them by the tab id for each. Then for each such group,preprocesses the specifications (including instrumentation and frames) and places this information into the tab database. This database update triggers the Reagent components responsible for rendering any such changes.\n\n### Extension tabs\n\nIn addition to 'standard tabs', those described above, the tab system may be extended with 'extension tabs'. The defining characteristic of such tabs is that they have an extra field: `[:opts :extfn]`. The value of this field must be a [Reagent form](https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md) function. Typically it will be a **form-2** and will implement the body of the tab. This functionality is seamlessly integrated with standard tabs and will be implicitly invoked when the tab is selected.\n\nExtension tabs are intended to be built and added on the client side. [add-tab](#tab-system) can be used to manually add such tabs, and the [:app-init user-msg](#connection) is a convenient place to perform this action (as part of any other application initialization). **NOTE** in [client only](#client-only-apps) you can explicitly fire this message as part of on page load code start.\n\n### Incremental bodies\n\nAs of version 0.9.0, there is support for _incrementally_ adding to a tab's [content area](#framework-topology-graphic), which is also known as it's _body_. Previously the only way to effect a change on a standard (non extention) tab's body was to perform a full update on it via [update-tabs](#tab-system) or more directly via [vis-list](#tab-system). Both of these required the full list of specifications to be rendered. There are applications where this is simply too restrictive. You may want to simply insert a [picture frame](#picture-frames) at a given point in a page. Or simply extend it, without needing to manage the entire sequence of specifications. There are now two new client core functions [add-to-tab-body](#tab-system) and [remove-from-tab-body](#tab-system) which can adjust the content of a tab at the granularity of picture frames.\n\n### Wrapping functions\n\nAs of version 0.9.0 there is now support for per tab user _wrapping functions_. This supports custom layout encompassing or encasing the tab's fully structured grid layout of the tab's sequence of specifications. Wrapping functions are configured by means of the `:wrapfn` key/value pair specifiable in the [tab's options](#meta-data-and-the-userdata-key). This provides a level of customization that is in between fully custom [extension tabs](#extension-tabs) and standard tabs. Wrapping functions must accept one argument, `gridhc`, which is the fully computed hiccup for the grid layout of the tab, and return a hiccup/re-com element which includes the `gridhc` element somewhere inside it (directly or in some child). The result will then be rendered in the tab's content area.\n\n\n## Sessions\n\nThe server side [start-server](#server-start) function has an `:idfn` parameter which is a parameterless function which returns session names for client connections. Upon a client connetion (initial websocket connect), the server will send a [registration](#connection) message to the client. Among other fields in the data sent will be a `:session-name`.  This is the name of the session group the client has been placed in according to the `idfn` function.\n\nIt is possible for the client to \"opt out\" of this session group in favor of another - including a totally new one of it's own making - by sending a [set session](#session-group) message to the server. This also implies that session groups need only contain one session.\n\nAs a simple example of how this can be done, the client [start](#client-start) function's default `header-fn` function uses a two stage input area to support changing the server's default assigned session group to some other such group (including a new one if the input does not name an existing group):\n\n```Clojure\n(defn set-session-name [name]\n  (let [old-uid (get-adb [:main :uid])\n        name (if (= name \"\") (old-uid :name) name)]\n    (sp/setval [sp/ATOM :main :session-name sp/ATOM] name app-db)\n    (when (not= name (old-uid :name))\n      (update-adb [:main :uid :name] name)\n      (send-msg {:op :set-session-name\n                 :data {:uid old-uid\n                        :new-name name}}))))\n\n(defn session-input []\n  (if (not= \"\" (sp/select-one [sp/ATOM :main :session-name sp/ATOM] app-db))\n    [:p]\n    [input-text\n     :model (get-adb [:main :session-name])\n     :on-change set-session-name\n     :placeholder (get-adb [:main :uid :name])\n     :width \"100px\"]))\n\n(defn default-header-fn []\n  [h-box :gap \"10px\" :max-height \"30px\"\n   :children [[gap :size \"5px\"]\n              [:img {:src (get-adb [:main :logo])}]\n              [title\n               :level :level3\n               :label [:span.bold (get-adb [:main :title])]]\n              [session-input]\n              [gap :size \"5px\"]\n              [title\n               :level :level3\n               :label [:span.bold (get-adb [:main :uid :name])]]\n              [gap :size \"30px\"]]])\n```\n\nOn connection the client's header contains a header with the default assigned session group name listed along side an input text box. The user can decide to change the assigned group, by typing in a new name. This will fire a [set-session-name](#session-group) message to the server to reassign this session to the desired group.\n\n![message overivew](resources/public/images/session-group-b4-aft.png?raw=true)\n\n\nAny message sent from the server to a named session group, will result in all current session members getting the message. In particular, messages updating the visualizations and picture frame content of tab bodies, will be sent to all such session members. This is equally true for application specific messages sent via [hmi/send-msg](#send-msg)\n\n\n## Messages\n\nThere are a few builtin messages which are sent between the server and client. These are generally in support of the framework aspects of Hanami. In general, domain specific applications will have a set of their own messages. These can be sent via [send-msg](#send-msg), available on both the client and server, and processed by [user-msg](#user-msg), also available on both the client and server\n\n\n![message overivew](resources/public/images/messages-overview.png?raw=true)\n\nThe messages listed in that graphic overview are roughly in the order they occur from top to bottom.  Let's walk through them so that we know what happens as they occur.\n\n### Connection\n\nUpon a client opening a connection, the server creates a session/app initialization data package, which is sent to the client via a `:register` message. This package is a map composed of two groups of data:\n\n* A standard set of fields and values:\n  - `:uid` a uid value `{:uuid (hmi/uuid), :name (idfn)}`\n    - `hmi/uuid` generates string names from uuids\n    - `idfn` is the [start-server](#server-start) function's `:idfn` argument, which is expected to return the applications [session group](#sessions) names.\n\n  - `:title`, the `start-server` function's `:title` argument\n  - `:logo`, the  `start-server` function's `:logo` argument\n  - `:img`, the  `start-server` function's `:img` argument\n  - `:opts`, the current `hc/default-opts` value\n\n* A, possibly empty, set of user/application specific fields. These are determined by the [start-server](#server-start) function's `:connfn` argument. This is a function of one parameter `(fn [x] ...)` where `x` is the map of standard fields and values. The expectation the function will compute new fields and values and add them to that set and return the total as a single initialization data map. The default function is `identity`.\n\nThe client side dispatches two calls upon reception of this message. Both are passed this final initialization data map. The first is `hc/register` which takes the standard fields and sets up the initial client side database. The second is the multimethod [user-msg](#user-msg) where the msg given is `{:op :app-init :data init-data-map}`. If your application implements this method for `:app-init`, it will be called and any application specific work can be done at that point.\n\n\n### Session group\n\nAs described in the section on [sessions](#sessions) and session groups, the client may change the group its session belongs to (named by the `idefn` function of [start-server](#server-start)) as described in the section on [connections](#connection). This is achieved by sending the server a `:set-session-name` message, which has the form:\n\n```Clojure\n{:op :set-session-name\n :data {:uid old-uid\n        :new-name name}}\n```\n\nWhere\n\n* `old-uid` is the uid the session currently has and wants shifted\n* `name` is the name of the session group the client wants to move to.\n\nUpon receipt of such a message, the server will update its database to reflect the change: the session's `uuid` will be moved to the requested group named by the `:new-name` field.\n\n\n### Tab updates\n\nThe `:tabs` message is the main way to update one or more tabs in a current sesion if you are using the [tab system](#tabs) in a client / server setup. The simplest way to send these messages is via the server [sv!](#server-core) function. Typically this will be from within a server side editor / IDE.\n\nIn [client only](#client-only-apps) applications, you can achieve the exact same effect by calling the client side 'mirror' function [sv!](#tab-system).\n\n As an example, the [simple cars](#simple-cars) case listed in the opening [examples](#examples) was dispatched as:\n\n```Clojure\n(-\u003e (hc/xform ht/point-chart\n      :UDATA \"data/cars.json\"\n      :X \"Horsepower\" :Y \"Miles_per_Gallon\" :COLOR \"Origin\")\n    hmi/sv!)\n```\n\n\n### User messages\n\nUser, also know here as \"application specific\", messages are sent via the [send-msg](#send-msg) function. This function exists in both the client and server `hmi` name space. The entire point of these messages is to support extension messages that are specific to the needs of an application. All such messgaes will be caught by the receiving party and dispatched to the multimethod [user-msg](#user-msg). There is one such message which is dispatched on the client side implicitly - the [:app-init](#connection) msg on client connection. This supports the any application specific client initialization processing.\n\nTwo examples of from [Saite](https://github.com/jsa-aerial/saite)\n\nThe [first](https://github.com/jsa-aerial/saite/blob/9aaacc3463604cfd03d239600dc859c48d6c27c7/src/cljs/aerial/saite/core.cljs#L258) is for the `:app-init` message.\n\n```Clojure\n(defmethod user-msg :app-init [msg]\n  (update-adb [:main :convert-chan] (async/chan))\n  (add-tab {:id :xvgl\n            :label \"\u003c-\u003e\"\n            :opts {:extfn (tab\u003c-\u003e :NA)}}))\n```\n\nSo, we add a Saite specific async channel to the database in support of conversions and popup renderings. Then we add the [extension tab](#extension-tabs) for the conversion / popup renderer capability.\n\nThe second ([client](https://github.com/jsa-aerial/saite/blob/9aaacc3463604cfd03d239600dc859c48d6c27c7/src/cljs/aerial/saite/core.cljs#L245) / [server](https://github.com/jsa-aerial/saite/blob/9aaacc3463604cfd03d239600dc859c48d6c27c7/src/clj/aerial/saite/core.clj#L81) is for messages specifically required by Saite to perform data conversion and transformation operations for the client.\n\n```Clojure\n;;; Client sends two versions of :read-clj msg to server.\n;;;  One for conversion and one for popup rendering\n;;; The popup case\n(let [...\n      msg {:op :read-clj\n           :data {:session-name nm\n                  :render? true\n                  :cljstg inspec}}\n      _ (send-msg msg)\n      ...]\n      ...\n      )\n\n;;; Server user-msg method for this:\n(defmethod hmi/user-msg :read-clj [msg]\n  (let [{:keys [session-name cljstg render?]} (msg :data)\n        clj (try\n              (let [clj (-\u003e\u003e cljstg clojure.core/read-string)]\n                (swap! dbg (fn[m] (assoc m :clj clj)))\n                (-\u003e\u003e clj xform-cljform eval final-xform))\n              (catch Exception e\n                {:error (format \"Error %s\" (or (.getMessage e) e))})\n              (catch Error e\n                {:error (format \"Error %s\" (or (.getMessage e) e))}))]\n    (swap! dbg (fn[m] (assoc m :xform-clj clj)))\n    (hmi/print-when [:pchan :umsg] :CLJ clj)\n    (hmi/send-msg session-name :clj-read (assoc clj :render? render?))))\n\n;;; NOTE the send-msg to client with :cli-read\n\n;;; Client user-msg method for this:\n(defmethod user-msg :clj-read [msg]\n  (let [data (msg :data)\n        render? (data :render?)\n        clj (dissoc data :render?)\n        result (if render?\n                 clj\n                 (try (-\u003e clj clj-\u003ejs (js/JSON.stringify nil, 2))\n                      (catch js/Error e (str e))))\n        ch (get-adb [:main :convert-chan])]\n    (go (async/\u003e! ch result))))\n```\n\n\n## Picture Frames\n\nPicture frames are simply a way to automatically encase your visualizations with 'meta' level instrumentation and / or arbitrary annotations. They are composed of four individual parts corresponding to the top, bottom, left, and right quadrants. Picture frames (or simply frames) are specified in the `usermeta` data of a specification via the `:frame` key. By default Hanami uses the substitution key `:USERDATA` for this. So, the format is:\n\n```Clojure\n{...\n :USERDATA {...\n            :frame {:fid ...\n                    :top ...\n                    :bottom ...\n                    :left ...\n                    :right ...}\n            ...\n            }\n}\n```\n\n![Hanami picture frame](resources/public/images/picture-frame-layout.png?raw=true)\n\nAll of the quadrants are optional and `:frame {}` is legal, which by the third [rule of transformation](#basic-transformation-rules) will result in its removal.\n\nThe value of `:fid` should be a globally unique id (keyword or string). If it is not specified the field will be removed and the frame will not have an id. This id will be used as the frame's DOM id as well as the corresponding spec's id. So, if it is not supplied the frame will not be accessible (in particular, it won't be accessible when using [incremental bodies](#incremental-bodies).\n\nThe value of a quadrant can be any legal mix of strings, hiccup, markdown (MD) and / or active components. Where 'legal' here means 'yields a legal DOM branch'. A great resource for active components, which Hanami provides as part of its package, is [Re-Com](https://github.com/Day8/re-com). This is especially true if you are not a CSS/flex and / or component savant.\n\nAs of version 0.5.0, picture frames may be ['empty'](#empty-frames) in that they do not need to have an associated visualization. To make this a bit simpler, there is a new template for these cases `ht/empty-chart`.\n\nAs of version 0.9.0, markdown is directly available via the `md` active component, as such it is an extension to the hiccup used by Hanami (from [Reagent](http://reagent-project.github.io/)). You would use this like / where you would use any other hiccup / re-com component. The form is `[md \u003coptional style map\u003e \u003cstring of markdown\u003e]`. The string can include newlines. The style map is for things such as font-size, color, etc.\n\n**ASSIDE**: LaTex is specifically _not_ included in Hanami. This decision is based on three reasons: 1. Many (most?) applications do not need LaTex math rendering.  2. [MathJax](https://www.mathjax.org/) is the go to JS engine for this and is a heavy dependency that would not be used in these applications.  3) React (as of version 16+) has a 'bug' preventing proper running of engines like MathJax (and Google Translate and many many other such rendering engines). It is possible to get around issue 3. but it requires 'monkey patching' React. If you need LaTex, you should consider using [Saite](https://github.com/jsa-aerial/saite) which is an application (built on Hanami) which does include MathJax, fixes 3., and is an application for general graphics and live document creation and sharing. Of course, if you are creating your own domain specific application and need LaTex for it, you will need to navigate issue 3. in your app. However, **note** that the `md` tag (see above markdown discussion) supports LaTex, so you can include it in your markdown (again, see Saite for examples).\n\nYou can specifiy frames from either the server or client side of your application. Working on the client side from within ClojureScript can make this more 'natural', as you are in the actual environment (browser/DOM) where things are running and rendering. However, specifying from the server side is fully supported, as long as Re-Com use is quoted (or more typical and useful, backquoted).\n\n**NOTE**: if you are _instrumenting_ your visualization (using active components - input box, dropdowns, selection lists, etc.) the _functions_ updating the relevant model(s) of / for these components _must_ be written over on the ClojureScript side (client). This is because, Hanami does not use a self hosted ClojureScript, but rather the cross compiled (with Google Closure) variant. Hence, you cannot, for example, write function code on the server side and have it eval'd on the client! (_NOTE_: this restriction may be lifted!)\n\nPicture frames are fully automatic if you use the default tab system. If you use custom tabs and want to make use of frames, you should call [hmi/vis-list](#client-core) on the client side (Cljs) for automatic rendering. If you are doing completely custom layouts, you will want to call [hmi/frameit](#client-core) client function. As always, if you do not want to use any of this, you should 'manually' use the [hmi/vgl](#client-core) reagent vega/vega-lite component.\n\nA couple of examples. These are actually taken from [Saite](https://github.com/jsa-aerial/saite), which is an interactive, exploratory and ad-hoc visualization application written with Hanami. Worth noting is that Saite assumes an interactive REPL model of exploration on the server side, pushing visualizations to the client. Hence, the only active components that can be used are those that are self contained, like information buttons or modal panels.\n\n\n```Clojure\n(let [_ (hc/add-defaults\n         :NAME #(-\u003e :SIDE % name cljstr/capitalize)\n         :STYLE hc/RMV)\n      frame-template {:frame\n                      {:SIDE `[[gap :size :GAP]\n                               [p {:style :STYLE}\n                                \"This is the \" [:span.bold :NAME]\n                                \" 'board' of a picture \"\n                                [:span.italic.bold \"frame.\"]]]}}\n      frame-top (hc/xform\n                 frame-template :SIDE :top :GAP \"10px\")\n\n      frame-left (hc/xform\n                  frame-template :SIDE :left :GAP \"10px\"\n                  :STYLE {:width \"100px\" :min-width \"50px\"})\n      frame-right (merge-with merge\n                   (hc/xform\n                    frame-template :SIDE :right :GAP \"2px\"\n                    :STYLE {:width \"100px\" :min-width \"50px\"})\n                   (hc/xform\n                    frame-template :SIDE :left :GAP \"2px\"\n                    :STYLE {:width \"100px\" :min-width \"50px\"\n                            :color \"white\"}))\n      frame-bot (hc/xform\n                 frame-template :SIDE :bottom :GAP \"10px\")]\n  (-\u003e\u003e (mapv #(hc/xform ht/point-chart\n                :HEIGHT 200 :WIDTH 250\n                :USERDATA (merge (hc/get-default :USERDATA) %)\n                :UDATA \"data/cars.json\"\n                :X \"Horsepower\" :Y \"Miles_per_Gallon\" :COLOR \"Origin\")\n             [frame-top frame-left frame-bot frame-right])\n       hmi/sv!))\n```\n\n![Hanami picture frame](resources/public/images/picture-frame-quads.png?raw=true)\n\n```Clojure\n(let [text \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes. Tamen a proposito, inquam, aberramus.\"\n      top `[[gap :size \"150px\"]\n            [p \"An example showing a \"\n             [:span.bold \"picture \"] [:span.italic.bold \"frame\"]\n             \". This is the top 'board'\"\n             [:br] ~text]]\n      left `[[gap :size \"10px\"]\n             [p {:style {:width \"100px\" :min-width \"50px\"}}\n              \"Some text on the \" [:span.bold \"left:\"] [:br] ~text]]\n      right `[[gap :size \"2px\"]\n              [p {:style {:width \"200px\" :min-width \"50px\"\n                          :font-size \"20px\" :color \"red\"}}\n               \"Some large text on the \" [:span.bold \"right:\"] [:br]\n               ~(.substring text 0 180)]]\n      bottom `[[gap :size \"200px\"]\n               [title :level :level3\n                :label [p {:style {:font-size \"large\"}}\n                        \"Some text on the \"\n                        [:span.bold \"bottom\"] [:br]\n                        \"With a cool info button \"\n                        [info-button\n                         :position :right-center\n                         :info\n                         [:p \"Check out Saite Visualizer!\" [:br]\n                          \"Built with Hanami!\" [:br]\n                          [hyperlink-href\n                           :label \"Saite \"\n                           :href  \"https://github.com/jsa-aerial/saite\"\n                           :target \"_blank\"]]]]]]]\n  (-\u003e\u003e [(hc/xform ht/point-chart\n          :TID :picframes\n          :TOP top :BOTTOM bottom :LEFT left :RIGHT right\n          :UDATA \"data/cars.json\"\n          :X \"Horsepower\" :Y \"Miles_per_Gallon\" :COLOR \"Origin\")]\n       hmi/sv!))\n```\n\n![Hanami picture frame](resources/public/images/picture-frame-all-quads-ex.png?raw=true)\n\n\n### Empty Frames\n\nAs of version 0.5.0, picture frames can now be 'empty', i.e., they do not need an associated visualization. The basic layout is the same as for frames with visualizations:\n\n![Hanami empty frame](resources/public/images/empty-picframe-layout.png?raw=true)\n\nTo make the use of these simpler (and easier) there is a new standard template `ht/empty-chart` which can be used to create them. Empty frames enable a more general document structure supporting paragraphs of html/'text oriented' material along side or in place of standard visualizations with or without frames. Here are a couple of examples showing the basic capability:\n\nThis example shows an empty frame with all 4 picture frame elements (areas) with various html/text information\n\n```Clojure\n(let [text \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes. Tamen a proposito, inquam, aberramus.\"\n      top `[[gap :size \"50px\"]\n            [p {:style {:width \"600px\" :min-width \"50px\"}}\n             \"An example empty picture frame showing all four areas.\"\n             \" This is the \" [:span.bold \"top\"] \" area. \"\n             ~text ~text ~text]]\n      left `[[gap :size \"50px\"]\n             [p {:style {:width \"300px\" :min-width \"50px\"}}\n              \"The \" [:span.bold \"left \"] \"area as a column of text. \"\n              ~text ~text ~text ~text]]\n      right `[[gap :size \"70px\"]\n              [p {:style {:width \"300px\" :min-width \"50px\"}}\n               \"The \" [:span.bold \"right \"] \"area as a column of text. \"\n               ~text ~text ~text ~text]]\n      bottom `[[gap :size \"50px\"]\n               [v-box\n                :children\n                [[p {:style {:width \"600px\" :min-width \"50px\"}}\n                  \"The \" [:span.bold \"bottom \"]\n                  \"area showing a variety of text. \"\n                  [:span.italic ~text] [:span.bold ~text]]\n                 [p {:style {:width \"400px\" :min-width \"50px\"\n                             :font-size \"20px\"}}\n                  \"some TeX: \" \"\\\\(f(x) = \\\\sqrt x\\\\)\"]\n                 [md {:style {:font-size \"16px\" :color \"blue\"}}\n                  \"#### Some Markup\n* **Item 1** Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n* **Item 2** Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes. Tamen a proposito, inquam, aberramus.\"]\n                 [p {:style {:width \"600px\" :min-width \"50px\"\n                             :color \"red\"}}\n                  ~text]]]]]\n  (-\u003e\u003e (hc/xform ht/empty-chart\n        :TID :picframes :TOP top :BOTTOM bottom :LEFT left :RIGHT right)\n       hmi/sv!))\n```\n\n![Hanami empty picframe](resources/public/images/picframe-empty.png?raw=true)\n\nThis next eample shows a tab/page with two picture frames. The left being a frame with an associated chart, while the one on the right shows how you can structure text in columns.\n\n```Clojure\n(let [text \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes. Tamen a proposito, inquam, aberramus.\"\n      top `[[gap :size \"50px\"]\n            [p \"Here's a 'typical' chart/plot filled picture frame.\"\n             \"It only has the top area\"\n             [:br] ~text]]\n      left `[[gap :size \"20px\"]\n             [p {:style {:width \"200px\" :min-width \"50px\"}}\n              \"This is an empty frame with a \" [:span.bold \"left \"]\n              \"column of text\" [:br] ~text ~text ~text ~text]]\n      right `[[gap :size \"30px\"]\n              [p {:style {:width \"200px\" :min-width \"50px\"}}\n               \"And a \" [:span.bold \"right \"]\n               \"column of text\"\n               [:br] ~text ~text ~text ~text]]]\n  (-\u003e\u003e [(hc/xform ht/point-chart\n          :TID :picframes :UDATA \"data/cars.json\"\n          :TOP top\n          :X \"Horsepower\" :Y \"Miles_per_Gallon\" :COLOR \"Origin\")\n        (hc/xform ht/empty-chart\n          :TID :picframes\n          :LEFT left :RIGHT right)]\n       hmi/sv!))\n```\n\n![Hanami picframe chart and empty](resources/public/images/picframe-chart-and-empty.png?raw=true)\n\n\n## Data Streaming\n\n\n## Client Only Apps\n\nWhile the most typical use case scenario involves clients and server working together, Hanami also enables the development of client only applications. In this case, the Clojure (server) side of the library (and framework aspects) is not used and all development is done on the client (typically browser) side.\n\nIn client only applications, there are no explicit messages, nor is the usual client side [start function](#client-start) used. Application initialization is no longer implicit (via the server to client [register](#connection) message); you will need to explicitly code your own specific initialization.\n\nAs noted in [resource requirements](#resource-requirements), you still need to create a landing page (`index.html`) with all the required resources and to have your directory structure setup for relative loading or use absolute URLs in the links.\n\nWhen running client only applications, the full [template system](#templates-substitution-keys-and-transformations) is available on the client side. Both the `hc` and `ht` namespaces may be required and their resources used. So, you can make full use of templates, substitution keys, and transformations. Further, all the default templates are available for use.\n\n\n### Basic client app requirements\n\n#### Require resources\n\nYou need to require the basic Hanami resources in your name space definition\n\n```Clojure\n(ns clientex.core\n  (:require\n  ...\n   [aerial.hanami.core\n    :as hmi\n    :refer [printchan user-msg\n            re-com-xref xform-recom\n            default-header-fn default-instrumentor-fn make-instrumentor\n            update-adb get-adb\n            init-tabs\n            hanami-main]]\n   [aerial.hanami.common :as hc :refer [RMV]]\n   [aerial.hanami.templates :as ht]\n   ...))\n```\n\nThe exact resources you refer will be up to how you typically do such referals. You may just use the `:as hmi` and reference all resources with `hmi/...`. If you want to use [re-com](https://github.com/Day8/re-com) capabilities, you will need to require those as well. For example, something like\n\n```Clojure\n [re-com.core\n    :as rcm\n    :refer [h-box v-box box gap line h-split v-split scroller\n            button row-button md-icon-button md-circle-icon-button info-button\n            input-text input-password input-textarea\n            label title p\n            single-dropdown\n            checkbox radio-button slider progress-bar throbber\n            horizontal-bar-tabs vertical-bar-tabs\n            modal-panel popover-content-wrapper popover-anchor-wrapper]]\n```\n\nBut again, what you require and refer will be up to what you need for your application and how you want to structure it.\n\n#### Application initialization\n\nYou will need to code your application setup and initialization functions. If you use the tab system you will need to call [init-tabs](#tab-system) as part of this. If you want to use [hanami-main](#hanami-main) you will also need to setup the database to have the expected fields. The [example client only app](https://github.com/jsa-aerial/hanami/tree/master/examples/ClientOnly) does this [here](https://github.com/jsa-aerial/hanami/blob/3a0e58d4342b499143ac6685b2bd29ca43750157/examples/ClientOnly/src/cljs/clientex/core.cljs#L44)\n\n#### Landing page - index.html\n\nThe most typical way of getting the application loaded is via an explict `index.html` file which is correctly located in the directory `resources/public` of your application. The example application uses this [index.html](https://github.com/jsa-aerial/hanami/blob/master/examples/ClientOnly/resources/public/Fig/index.html)\n\nOnce you have your code compiled and located per the `\u003cscript ...\u003e` tag in your `index.html`, you can simply use a browser to navigate to this file and your application will be active. If you want to have an interactive session(s) with the application you will need to run [figwheel](https://github.com/bhauman/lein-figwheel) or an equivalent type of capability. The client only [example app](https://github.com/jsa-aerial/hanami/tree/master/examples/ClientOnly) is set up to use figwheel (plus cljs-repl and piggyback)\n\n#### Application start\n\nYour code should have a final block which will start the application upon page load. Generally this will test for a specific DOM element upon which to render (via reagent) your main component. An example of this from the client only example is [here](https://github.com/jsa-aerial/hanami/blob/3a0e58d4342b499143ac6685b2bd29ca43750157/examples/ClientOnly/src/cljs/clientex/core.cljs#L76). In this case the startup first sets several default [substitution keys](#example-predefined-substitution-keys), then calls [app-init](https://github.com/jsa-aerial/hanami/blob/3a0e58d4342b499143ac6685b2bd29ca43750157/examples/ClientOnly/src/cljs/clientex/core.cljs#L62) which sets up the application. At this point you will be able to render visualizations just like from the server, using the same [hc/xform](#templates-and-substitution-keys) transformer of templates, and [hmi/sv!](#tab-system)\n\n\n### Example Client Only Application\n\nHanami's github repository contains a client only example application. Found in the `examples` directory [here](https://github.com/jsa-aerial/hanami/tree/master/examples/ClientOnly). To run this you can simply change directory to the `examples/ClientOnly` directory. Then run `lein repl`. When repl is up and running, issue `(use 'figwheel-sidecar.repl-api)` and when ready issue `(start-figwheel!)`. This will open a browser window and you will see:\n\n![Hanami pic 1](resources/public/images/client-only-example-app.png?raw=true)\n\nYou can also, at this point, pull up the `clientex.core.cljs` in emacs, connect to the repl, and once connected issue `(cljs-repl)`. Switch buffers to the `clientex.core` name space file, and evaluate the buffer. At this point you will be able to interactively transform and render other specifications (see the 'rich' [comment](https://github.com/jsa-aerial/hanami/blob/6d52c0cc27894969aab0597362f5a1f04572d654/examples/ClientOnly/src/cljs/clientex/core.cljs#L108) for some other examples)\n\n\n\n\n## API\n\nAs noted, with respect to abstracting visualizations (ala' something like [Altair](https://altair-viz.github.io/)) there isn't much of an API and no classes, objects, or methods are involved. Most of what API there is centers on application development and start up. This is split across the server and the client side.\n\n\n### Templates and Substitution keys\n\nThis applies across both the server and client - the facilities are available in both server and client. They are in `aerial.hanami.common`, in this documentation aka `hc`. **NOTE** this namespace, along with `aerial.hanami.templates` (aka `ht`), is available on _both_ the client and server.\n\n* `(defn update-subkeyfns [k vfn \u0026 kvfns])`: Updates the [substitution key function map](#subtitution-key-functions). `k` is a substitution key, `vfn` is either a function `(fn [submap, subkey, subval] ...)` or the special `hc/RMV` value. The latter case will _remove_ any current key `k` and its value.\n\n* `(defn update-defaults [k v \u0026 kvs])`: Updates the default [substitution key map](#example-predefined-substitution-keys). `k` is a substitution key, `v` is some appropriate value including `hc/RMV`.  To remove a key from the global register use the namespace key `::ht/RMV` = `:aerial.hanami.templates/RMV` for `v` which will remove the key.\n\n* `(defn get-default [k])`: Returns the current default value of substitution key `k`.\n\n* `(defn get-defaults [k \u0026 ks])`: Returns the current default value of substitution key `k` or a vector of [k v] pairs if given two or more ks.\n\n* `(defn xform ([spec submap]) ([spec k v \u0026 kvs]) ([spec]))`: The template/specification transformation function. Generally, you will only call the second two arity signatures. The first is used for recursive transformation actions, so in the first signature, `spec` is generally the current value of a recursive transformation and submap the current state of the transformation.\n\n  In the second and third signatures, `spec` is a template. [Recall](#templates-substitution-keys-and-transformations) legal templates may be already complete fully realized Vega or Vega-Lite specifications - this latter case is for what the third signature is intended. The second signature is what you will use in nearly all cases. `k` is a substitution key and `v` is the value it will have in the transformation. Hence you can override defaults and [template local defaults](#template-local-defaults) and add specific keys for just a given transformation. Returns the fully transformed final value.\n\n  There are currently (as of V0.15.1) three transformation control keys.  These keys affect the transformation process by changing the flow or state of the transformation.\n\n    - `:aerial.hanami.common/use-defaults?` : if given as `true` (the default value) the global register is used as a starting set of default substitution keys and values.  If `false` only the in line kvs and any template local defaults are used.\n\n    - `:aerial.hanami.common/rmv-empty?` : If `true`, the default, follow the [third rule](#basic-transformation-rules) of transformations.  If `false` do **not** remove empty collections.\n\n    - `:aerial.hanami.templates/defaults` : If a template (actually, any map) has this key defined, the value map for it is merged into the current state.  This effects the behaivor of [template local defaults](#template-local-defaults)\n\n\n\n### Startup\n\nThere are two main start functions. One each for the server and client.\n\n#### Server start\n```Clojure\n(defn start-server\n  [port \u0026 {:keys [route-handler idfn title logo img connfn]\n           :or {route-handler (hanami-handler (hanami-routes))\n                idfn (partial gensym \"hanami-\")\n                connfn identity\n                title \"花見 Hanami\"\n                logo \"logo.png\"\n                img \"Himeji_sakura.jpg\"}}]\n  ...)\n```\n\n* `port` the port on which to open the websocket messaging system\n\n* `:route-handler` a function for handling http route requests. Hanami uses http-kit and this will be the function passed as the `app` argument to its `run-server`. Currently Hanami directly supports building routes via [compojure](https://github.com/weavejester/compojure), though supporting [bidi](https://github.com/juxt/bidi) is being considered. There are three ancillary functions to support users in creating their application routes.\n\n  - `(defn landing-page [request index-path] ...)` The `request` argument is an http request map, but is not used. `index-path` is the resource path to your `index.html` landing page. Returns a Ring response map: `(content-type {:status 200, :body (io/input-stream (io/resource index-path))}, \"text/html\")`\n\n  - `(defn hanami-routes [\u0026 {:keys [landing-handler index-path]\n                             :or {landing-handler landing-page\n                                  index-path \"public/index.html\"}}] ...)` Creates a set of routes that uses `(landing-handler request index-path)` as the value of the `get /` route, and adds the necessary websocket routing to this and finally adds the default resources route `(compojure.route/resources \"/\")`. Returns the resulting function implementing the routes. Uses `compojure.core/routes` to create the 'rounting function'.\n\n  - `(defn hanami-handler [routes-fn \u0026 middle-ware-stack] ...)` Takes a routing function `routes-fn` (as built by `hanami-routes`) and zero or more ring middle-ware wrapping functions. Returns the full wrapped site handler function.\n\n* `:idfn` Function of zero paramters. The [session group](#sessions) name generator for connections\n\n* `:connfn` Function of one parameter (fn [x] ...) where x is the map of standard fields and values of a [connection registration](#connection). Should compute any necessary application specific registration data. This data will be available to the `:app-init` method of [user-msg](#user-messages) multimethod.\n\n* `:title` A application appropriate title. Can be used by the [client start](#client-start) `header-fn`. The default header function of the client uses this to make a title in the [application header area](#framework-topology-graphic)\n\n* `:logo` A glyph/avatar for use in header function processing\n\n* `img` An image resource. Again for use in application initialization as background or other such use.\n\n\n#### Client start\n```Clojure\n(defn start [\u0026 {:keys [elem port header-fn instrumentor-fn frame-cb]\n                :or {header-fn default-header-fn\n                     instrumentor-fn default-instrumentor-fn\n                     frame-cb default-frame-cb\n                     symxlate-cb identity}}]\n  ...)\n```\n\n* `:elem` the DOM element on which the main Reagent component will be rendered. For example `(js/document.querySelector \"#app\")`. If using the framwork default, this will be the element on which [hanami-main](#hanami-main) is rendered.\n\n* `:port` the port to make websocket connection with. Generally as part of your page load start code, this port will be determined by `:port js/location.port`.\n\n* `:header-fn` a parameterless function which is intended to perform layout of an application's [header](#header) [area](#framework-overview)\n\n* `:instrumentor-fn` A function `(fn[{:keys [tabid spec opts]}] ...)` which is used to implement custom external instrumentation for visualizations. Instrumentation are active components and typically will be re-com components. See [barchart example](#instrumented-barchart) for an example. Also, CLJS `aerial.hanami.core` has a 'rich' [comment](https://github.com/jsa-aerial/hanami/blob/ee5556d41033a956ef07d3971f9a506e1465fef9/src/cljs/aerial/hanami/core.cljs#L694) which gives a couple full examples. The `default-instrumentor-fn` is a no-op.\n\n* `:frame-cb` A function `(fn ([]...) ([spec frame] ...))` which used to implement post processing of frames. The `[]` case is called on component update. The two arity case is called by [frameit](#visualization) and passed the `spec` and `frame map` containing the frame elements that will be built into a single frame and `:frameid` whose value is the HTML id for the frame. It must passback a `frame map`, which can be the input or a new one with some adjustments. Generally this is used to set up post _mounting_ processing (DOM mutation...) and the input is simply returned. The zero arity case is strictly intended for post mount processing. As an example, [Saite](https://github.com/jsa-aerial/saite#user-tabs) uses this to render LaTex via MathJax.\n\n* `:symxlate-cb` A function `(fn ([sym] ...)` which will be called during translation of symbols in hiccup/re-com vectors (typically from server, but could be client only as well). `sym` is a symbol in such a vector which does _not_ exist in the default translation table. The return should be an application specific value (normally a function defined in the application) or `sym` if no such value exists for the application. The idea here is that users may add extra Reagent _components_ which can then be used in picture frame construction. So, you can use the symbol as denoting a legal component and it will be replaced by the value returned by your `symxlate-cb` function. This will then be used during overall rendering.\n\n\n### Message system\n\nThis applies across both the server and client - the facilities are available in both server and client. They are in `aerial.hanami.core`, in this documentation aka `hmi`. **NOTE** this namespace, exists on _both_ the client and server.\n\n#### send msg\n\n* Server: `(defn send-msg ([to app-msg] ...) ([to op data] ...))`\n  - `to` is one of\n     - string naming an existing [session group](#sessions)\n     - string naming an active Hanami uuid, a [session uuid](#sessions)\n     - an active client websocket object\n\n  - `app-msg` is\n     - an application specific message with form `{:op opkey, :data data-value}`\n       - `opkey` is a msg specific operator\n       - `data-value` is some arbitrary data - typically a map of fields\n\n  - `op` is an `opkey` and `data` a `data-value`. This form is converted to a call `(send-msg to {:op op, :data data})`\n\n\n* Client: `(defn send-msg ([app-msg] ...) ([op data] ...))`\n  - `app-msg` is\n     - an application specific message with form `{:op opkey, :data data-value}`\n       - `opkey` is a msg specific operator\n       - `data-value` is some arbitrary data - typically a map of fields\n\n  - `op` is an `opkey` and `data` a `data-value`. This form is converted to a call `(send-msg to {:op op, :data data})`\n\nIn both cases, the _receiving_ party will have their [user-msg](#user-msg) multimethod dispatched on the `msg op key`.\n\n\n#### user msg\n\n* Client and Server `(defmulti user-msg :op)`\n  Multimethod for encoding application specific message envelopes (see [Hanasu](https://github.com/jsa-aerial/hanasu) for general details). Specifically, calls with appropriate message arguments to [send-msg](#send-msg) on either the client or server will produce a dispatch in the corresponding party to this multimethod. Intent and purpose of these is to support domain semantics of specific applications. See for example, [Saite](https://github.com/jsa-aerial/saite).\n\n\n### Client core\n\n#### Visualization\n\n* `(defn visualize [spec elem] ...)`: Function used by `vgl` reagent component to create Vega and Vega-Lite visualizations.\n  - `spec` is a Vega or Vega-Lite specification _as Clj/EDN_ which must have a `:usermeta` field with at least the [opts](#meta-data-and-the-userdata-key) field whose value must include at least the `:mode`.\n  - `elem` is the DOM element into which the visualization will be inserted.\n\n* `(defn vgl [spec] ...)`: Reagent [Form-3](https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md) which has life cycle methods for mounting, updating, and rendering a visualization.\n  - `spec` is a Vega or Vega-Lite specification _as Clj/EDN_ which must have a `:usermeta` field with at least the [opts](#meta-data-and-the-userdata-key) field whose value must include at least the `:mode`.\n\n* `(defn frameit [spec frame] ...)`: Embed visualization inside a [picture frame](#picture-frames).\n  - `spec` is a Vega or Vega-Lite specification _as Clj/EDN_ which must have a `:usermeta` field with at least the [opts](#meta-data-and-the-userdata-key) field whose value must include at least the `:mode`.\n  - `frame` is the complete prebuilt picture frame with all 4 components: `:top`, `:bottom`, `:left`, and `:right`. One or more of these may be visually empty (for example, `[]` for `:top` and `:bottom` and `[[box :size \"0px\" :child \"\"]]` for `:left` and `:right`.\n\n* `(defn vis-list [tabid spec-frame-pairs opts] ...)`: Function for laying out [tab](#tabs) bodies. Used for both standard and [extension tabs](#extension-tabs) Implicitly called by via reactive update events. May be manually called.\n\n\n#### Tab system\n\n* `(defn get-tab-field ([tid] ...) ([tid field] ...))`: For `[tid]` return entire tab value associated with tab id `tid`. For `[tid field`, return the field value associated with key `field` in tab value associated with tab id `tid`.\n\n* `(defn update-tab-field [tid field value] ...)`: Change (or add) the value of the key `field` in tab value associated with tab id `tid` to the value `value`.\n\n* `(defn get-cur-tab ([]...) ([field]...))`: For `[]` return the tab value of the current tab. For `[field]`, return the value of the key `field` in current tab.\n\n* `(defn set-cur-tab [tid] ...)`: Change the current tab to be that associated with tab id `tid`\n\n* `(defn update-cur-tab [field value] ...)`: Update the value of the key `field` to the value `value` in the current tab\n\n* `(defn add-tab [tabval] ...)`: Add a tab to the tab bar. `tabval` is a complete tab value:\n```Clojure\n{:label \u003ctab label name\u003e\n :id \u003ctab id\u003e\n :opts \u003cfull options - see hc/default-opts\u003e\n :specs \u003ccurrent set of specs for tab body\u003e\n :spec-frame-pairs \u003ccurrent set of (spec,frame) pairs rendered in body\u003e\n }\n```\n\n* `(defn replace-tab [tid newdef] ...)`: Like `add-tab`, but replaces the tab value associated with tab id `tid` with `newdef`.\n\n* `(defn del-tab [tid] ...)`: Delete the tab associated with tab id `tid`. If this is the current tab, set current tab to the first remaining tab. If this deletes the only tab, remove tab bar.\n\n* `(defn add-to-tab-body [tid picframe \u0026 {:keys [at position opts] :or {at :end position :after opts {}}}] ...)`: Adds to tab id `tid`'s body (content area) a new specification (with a defined picture frame) `picframe` at location `at`, either an existing frame id or special designator `:end`, positioned `position` (either `:before` or `:after`) relative to `at`. If `opts` is supplied it must be a map containing [tab options](#meta-data-and-the-userdata-key) which will override any existing options for the tab. The content area will properly reflect the grid layout for the tab. **Note**, if `opts` specifies a new grid layout, it will be used.\n\n* `(defn remove-from-tab-body [tid fid] ...)`: Removes the picture frame with frame id `fid` from the body (content area) of tab with id `tid`.\n\n* `(defn init-tabs [] ...)`: Initialize the tab system. This is called implcitly when using the client [start](#client-start), via the [:register](#connection) message. If you are building a client only application, and want to use the tab system, you need to call this as part of your initialization.\n\n* `(defn active-tabs [] ...)`: **Reagent component** that sets up the tab bar area. The content area will properly reflect the grid layout for the tab.\n\n* `(defn tabs [] ...)`: **Reagent component** for driving the tab system. This is called impicitly when using [hanami main](#hanami-main) Reagent component. In particular, drives the update, rendering, and display of tab bodies. May be called manually or part of a different main component.\n\n* `(defn update-tabs [specs] ...)`: Function to update the set of tabs. This includes adding new tabs to the set. `specs` is a vector of specifications, each of which must have a [:usermeta](#meta-data-and-the-userdata-key) field with associated `:tab` key and value, which must contain an `:id` field (see [USERDATA](#meta-data-and-the-userdata-key) for details). The specifications in `specs` do **not** need to have the same tab given - they may indicate a mix of tabs. As such, `update-tabs` first groups the specifications by tab id, preprocesses the specs in each set and updates the tab database to reflect the changes. This database update triggers he Reagent components `active-tabs` and `tabs` to fire and render the changes. Typically, in server based applications, this function is implicitly called due to the reception of [:tabs](#tab-updates) messages. For [client](#client-only-apps) only applications, this may be explicitly called, thus invoking the tab system machinery or more cleanly invoked by using the client side `sv!` which mirrors the server [sv!](#server-core).\n\n* `(defn sv! [specs] ...)`: Invokes `update-tabs` after ensuring `specs` is a vector of specifications (`spec` can be a single specification and it will be wrapped in a vector). Provided in order to keep client and server side tab updating the same in look and feel.\n\n\n#### Hanami main\n\nThe main (top level) Reagent component for driving an application using the standard [framework aspects](#framework-overview)\n\n* `(defn hanami-main [] ...)`: Top level **Reagent component**. Renders via `reagent/render` as part of standard [registration](#connections). This component is the main driver for stanard framework usage. It assumes the standard framework [structure](#framework-topology-graphic) and therefore also assumes that the `app-db` has been initialized according to the [register](#connection) function.\n\nCreates a Re-Com [v-box](https://re-com.day8.com.au/#/v-box) consisting of the framework `header` (using the value of calling the client start [header](#header) function), `tab bar` area (via the [active-tabs](#tab-system) component, and `tab body` area (via the [tabs](#tab-system) component). Each is separated by a [line](https://re-com.day8.com.au/#/line).\n\nIf you are writing a [client only](#client-only-apps) application you could render this as part of your applications initialization if you want to use Hanami framework capabilities.\n\n\n### Server core\n\n* `(defn sv! [specs] ...)`: `specs` is a single specification (which will be wrapped in a vector before processing) or a vector of specifications, each of which must include [:usermeta](#meta-data-and-the-userdata-key) data with full [tab data](#tabs), where the [msgop](#meta-data-and-the-userdata-key) must be `:tabs` and the [session-name](#sessions) must be appropriately set. Specifications must be fully transformed! Constructs a [:tabs](#tab-updates) message and sends to client, which will add (or update) the bodies of the tabs to reflect the specifications given to them.\n\n\n# Example Transform 'Gallery'\n\nA gallery of visualization examples exploiting the abstraction power of Hanami's templates and recursive transformations. This will likely keep growing as neat new things are built.\n\n** If anyone has their own nice examples that they would like to pass along, please don't hesitate to submit a PR!!**  Thanks!\n\n## Observed vs Binomial Model\n\nHere is the same data (observed distribution vs binomial models) as row and column grouped (faceted) charts.\n\n```Clojure\n(hc/xform ht/grouped-bar-chart\n  :TITLE \"Real distribution vs Binomials\", :TOFFSET 10\n  :HEIGHT 80, :WIDTH 450\n  :DATA ...\n  :X \"cnt\" :XTYPE \"ordinal\" :XTITLE \"\"\n  :Y \"y\" :YTITLE \"Probability\"\n  :COLOR ht/default-color :CFIELD :ROW :CTYPE :ROWTYPE\n  :ROW \"dist\" :ROWTYPE \"nominal\")\n\n(hc/xform ht/grouped-bar-chart\n  :TITLE \"Real distribution vs Binomials\", :TOFFSET 40\n  :WIDTH (-\u003e 550 (/ 6) double Math/round (- 15))\n  :DATA ...\n  :X \"dist\" :XTYPE \"nominal\" :XTITLE \"\"\n  :Y \"y\" :YTITLE \"Probability\"\n  :COLOR ht/default-color\n  :COLUMN \"cnt\" :COLTYPE \"ordinal\")\n```\n\nBoth of these transform into similar amounts of VGL output, but the first is somewhat more interesting. Note the `:color` mark propery value and the input  for it in the above code.\n\n```Clojure\n{:title {:text \"Real distribution vs Binomials\", :offset 10},\n :height 80,\n :width 450,\n :background \"floralwhite\",\n :mark \"bar\",\n :encoding\n {:x {:field \"cnt\", :type \"ordinal\", :axis {:title \"\"}},\n  :y {:field \"y\", :type \"quantitative\", :axis {:title \"Probability\"}},\n  :row {:field \"dist\", :type \"nominal\"},\n  :color\n  {:field \"dist\",\n   :type \"nominal\",\n   :scale {:scheme {:name \"greenblue\", :extent [0.4 1]}}},\n  :tooltip\n  [{:field \"cnt\", :type \"ordinal\"}\n   {:field \"y\", :type \"quantitative\"}]},\n  :view {:stroke \"transparent\"},\n  :axis {:domainWidth 1}},\n :data {:values ...}\n :config\n {:bar {:binSpacing 0, :discreteBandSize 1, :continuousBandSize 1}}\n```\n\nAnd the rendered visualizations are:\n\n![Hanami pic 4.1](resources/public/images/real-vs-binomial-col.png?raw=true)\n\n![Hanami pic 4.2](resources/public/images/real-vs-binomial-row.png?raw=true)\n\n\nThis is a nice example of how one visualization (the row grouping) can bring out the salient information so much better than another (the col grouping)\n\n\n## Lowess smoothing of Tn-Seq fitness data\n\nThe next is a visualization for an investigation into using lowess smoothing of Tn-Seq fitness data.\n\n```Clojure\n(hc/xform ht/layer-chart\n  :TITLE \"Raw vs 1-4 lowess smoothing\" :TOFFSET 5\n  :HEIGHT 500 :WIDTH 700\n  :DATA (concat base-xy lowess-1 lowess-2 lowess-3 lowess-4)\n  :LAYER (mapv #(hc/xform ht/gen-encode-layer\n                  :MARK (if (= \"NoL\" %) \"circle\" \"line\")\n                  :TRANSFORM [{:filter {:field \"NM\" :equal %}}]\n                  :COLOR \"NM\"\n                  :XTITLE \"Position\", :YTITLE \"Count\")\n               [\"NoL\" \"L1\" \"L2\" \"L3\" \"L4\"]))\n```\n\nThis one is interesting in that it combines some nice straight ahead Clojure data mapping with the template system. Here, we create five layers - but they are all different data sets and so VGL's `repeat` would not apply. But, Clojure's `mapv` combined with Hanami's `xform` does the trick. Of course, any number of layers could be so constructed.\n\n\n```Clojure\n{:title {:text \"Raw vs 1-4 lowess smoothing\", :offset 5},\n :height 500,\n :width 700,\n :background \"floralwhite\",\n :layer\n [{:transform [{:filter {:field \"NM\", :equal \"NoL\"}}],\n   :mark \"circle\",\n   :encoding\n   {:x {:field \"x\", :type \"quantitative\", :axis {:title \"Position\"}},\n    :y {:field \"y\", :type \"quantitative\", :axis {:title \"Count\"}},\n    :color {:field \"NM\", :type \"nominal\"},\n    :tooltip\n    [{:field \"x\", :type \"quantitative\"}\n     {:field \"y\", :type \"quantitative\"}]}}\n  {:transform [{:filter {:field \"NM\", :equal \"L1\"}}],\n   :mark \"line\",\n   :encoding\n   {:x {:field \"x\", :type \"quantitative\", :axis {:title \"Position\"}},\n    :y {:field \"y\", :type \"quantitative\", :axis {:title \"Count\"}},\n    :color {:field \"NM\", :type \"nominal\"},\n    :tooltip\n    [{:field \"x\", :type \"quantitative\"}\n     {:field \"y\", :type \"quantitative\"}]}}\n  {:transform [{:filter {:field \"NM\", :equal \"L2\"}}],\n   :mark \"line\",\n   :encoding\n   {:x {:field \"x\", :type \"quantitative\", :axis {:title \"Position\"}},\n    :y {:field \"y\", :type \"quantitative\", :axis {:title \"Count\"}},\n    :color {:field \"NM\", :type \"nominal\"},\n    :tooltip\n    [{:field \"x\", :type \"quantitative\"}\n     {:field \"y\", :type \"quantitative\"}]}}\n  {:transform [{:filter {:field \"NM\", :equal \"L3\"}}],\n   :mark \"line\",\n   :encoding\n   {:x {:field \"x\", :type \"quantitative\", :axis {:title \"Position\"}},\n    :y {:field \"y\", :type \"quantitative\", :axis {:title \"Count\"}},\n    :color {:field \"NM\", :type \"nominal\"},\n    :tooltip\n    [{:field \"x\", :type \"quantitative\"}\n     {:field \"y\", :type \"quantitative\"}]}}\n  {:transform [{:filter {:field \"NM\", :equal \"L4\"}}],\n   :mark \"line\",\n   :encoding\n   {:x {:field \"x\", :type \"quantitative\", :axis {:title \"Position\"}},\n    :y {:field \"y\", :type \"quantitative\", :axis {:title \"Count\"}},\n    :color {:field \"NM\", :type \"nominal\"},\n    :tooltip\n    [{:field \"x\", :type \"quantitative\"}\n     {:field \"y\", :type \"quantitative\"}]}}],\n :data {:values ...}\n :config\n {:bar {:binSpacing 1, :discreteBandSize 5, :continuousBandSize 5}}}\n ```\n\n![Hanami pic 5](resources/public/images/lowess-tnseq-smoothing.png?raw=true)\n\n\n### With Overview + Detail\n\nWe can do something more interesting here in this case, as we may want to get close ups of various sections of such a plot. Instead of looking at the entire genome, we can focus on chunks of it with selection brushes using an overlay+detail display:\n\n```Clojure\n(hc/xform ht/vconcat-chart\n   :TITLE \"Raw vs 1-4 lowess smoothing\" :TOFFSET 5\n   :DATA (concat base-xy lowess-1 lowess-2 lowess-3 lowess-4)\n   :VCONCAT [(hc/xform ht/layer-chart\n               :LAYER\n               (mapv #(hc/xform\n                       ht/gen-encode-layer :WIDTH 1000\n                       :MARK (if (= \"NoL\" %) \"circle\" \"line\")\n                       :TRANSFORM [{:filter {:field \"NM\" :equal %}}]\n                       :XSCALE {:domain {:selection \"brush\"}}\n                       :COLOR \"NM\", :XTITLE \"Position\", :YTITLE \"Count\")\n                     [\"NoL\" \"L1\" \"L2\" \"L3\"]))\n             (hc/xform ht/gen-encode-layer\n               :MARK \"circle\"\n               :WIDTH 1000, :HEIGHT 60\n               :TRANSFORM [{:filter {:field \"NM\" :equal \"NoL\"}}]\n               :SELECTION {:brush {:type \"interval\", :encodings [\"x\"]}}\n               :COLOR \"NM\" :XTITLE \"Position\", :YTITLE \"Count\")])\n```\n\nHere are two snapshots of the resulting interactive visualization:\n\n![Hanami pic 5.1](resources/public/images/lowess-overlay-detail-1.png?raw=true)\n![Hanami pic 5.2](resources/public/images/lowess-overlay-detail-2.png?raw=true)\n\n\n## Interactive Cross plot Differential Gene Expression\n\nAnd lastly a quite involved example from a real application for RNASeq Differential Gene Expression:\n\n```Clojure\n(let [data (-\u003e\u003e DGE-data (filter #(-\u003e \"padj\" % (\u003c= 0.05))))\n       mdwn-brush (hc/xform ht/interval-brush-mdwn :MDWM-NAME \"brush\" :IRESOLVE \"global\")\n       color {:field \"NM\" :type \"nominal\" :scale {:range [\"#e45756\" \"#54a24b\" \"#4c78a8\"]}}\n       size {:condition {:selection {:not \"brush\"} :value 40} :value 400}\n       tooltip `[{:field \"Gene\", :type \"nominal\"} ~@ht/default-tooltip {:field \"pvalue\", :type \"quantitative\"}]]\n   (hc/xform\n    ht/hconcat-chart\n    :TITLE\n    \"RNASeq Exp 180109_NS500751_0066 DGE for aT4-024V10min vs aT4-024V30min\"\n    :TOFFSET 40\n    :DATA :data\n    :HCONCAT[(hc/xform\n              ht/point-chart\n              :TITLE \"MA Plot\"\n              :MSIZE 40\n              :SELECTION (merge  (hc/xform ht/interval-scales :INAME \"grid1\")\n                                 mdwn-brush)\n              :X \"baseMean\", :Y \"log2FoldChange\"\n              :COLOR color, :SIZE size, :TOOLTIP tooltip)\n             (hc/xform\n              ht/point-chart\n              :TITLE \"Volcano Plot\"\n              :MSIZE 40\n              :SELECTION (merge (hc/xform ht/interval-scales :INAME \"grid2\")\n                                mdwn-brush)\n              :X \"log2FoldChange\", :Y \"-log10(pval)\"\n              :COLOR color, :SIZE size :TOOLTIP tooltip)]))\n```\n\nTransforms to:\n\n```Clojure\n{:hconcat\n [{:encoding\n   {:x {:field \"baseMean\", :type \"quantitative\"},\n    :y {:field \"log2FoldChange\", :type \"quantitative\"},\n    :color\n    {:field \"NM\",\n     :type \"nominal\",\n     :scale {:range [\"#e45756\" \"#54a24b\" \"#4c78a8\"]}},\n    :size\n    {:condition {:selection {:not \"brush\"}, :value 40}, :value 400},\n    :tooltip\n    [{:field \"Gene\", :type \"Nominal\"}\n     {:field \"baseMean\", :type \"quantitative\"}\n     {:field \"log2FoldChange\", :type \"quantitative\"}\n     {:field \"pvalue\", :type \"quantitative\"}]},\n   :mark {:type \"circle\", :size 40},\n   :width 450,\n   :background \"floralwhite\",\n   :title {:text \"MA Plot\"},\n   :selection\n   {\"grid1\"\n    {:type \"interval\",\n     :bind \"scales\",\n     :translate\n     \"[mousedown[event.shiftKey], window:mouseup] \u003e window:mousemove!\",\n     :encodings [\"x\" \"y\"],\n     :zoom \"wheel!\",\n     :resolve \"global\"},\n    \"brush\"\n    {:type \"interval\",\n     :on\n     \"[mousedown[!event.shiftKey], window:mouseup] \u003e window:mousemove!\",\n     :translate\n     \"[mousedown[!event.shiftKey], window:mouseup] \u003e window:mousemove\",\n     :resolve \"global\",\n     :mark {:fill \"#333\", :fillOpacity 0.125, :stroke \"white\"}}},\n   :height 400}\n  {:encoding\n   {:x {:field \"log2FoldChange\", :type \"quantitative\"},\n    :y {:field \"-log10(pval)\", :type \"quantitative\"},\n    :color\n    {:field \"NM\",\n     :type \"nominal\",\n     :scale {:range [\"#e45756\" \"#54a24b\" \"#4c78a8\"]}},\n    :size\n    {:condition {:selection {:not \"brush\"}, :value 40}, :value 400},\n    :tooltip\n    [{:field \"Gene\", :type \"Nominal\"}\n     {:field \"log2FoldChange\", :type \"quantitative\"}\n     {:field \"-log10(pval)\", :type \"quantitative\"}\n     {:field \"pvalue\", :type \"quantitative\"}]},\n   :mark {:type \"circle\", :size 40},\n   :width 450,\n   :background \"floralwhite\",\n   :title {:text \"Volcano Plot\"},\n   :selection\n   {\"grid2\"\n    {:type \"interval\",\n     :bind \"scales\",\n     :translate\n     \"[mousedown[event.shiftKey], window:mouseup] \u003e window:mousemove!\",\n     :encodings [\"x\" \"y\"],\n     :zoom \"wheel!\",\n     :resolve \"global\"},\n    \"brush\"\n    {:type \"interval\",\n     :on\n     \"[mousedown[!event.shiftKey], window:mouseup] \u003e window:mousemove!\",\n     :translate\n     \"[mousedown[!event.shiftKey], window:mouseup] \u003e window:mousemove\",\n     :resolve \"global\",\n     :mark {:fill \"#333\", :fillOpacity 0.125, :stroke \"white\"}}},\n   :height 400}],\n :config\n {:bar {:binSpacing 1, :discreteBandSize 5, :continuousBandSize 5}},\n :width 450,\n :background \"floralwhite\",\n :title\n {:text\n  \"RNASeq Exp 180109_NS500751_0066 DGE for aT4-024V10min vs aT4-024V30min\",\n  :offset 40},\n :height 400,\n :data {:values [... ]}}\n```\n\nWell, now that is quite a lot. When sent to a view, visualizes as follows. Note that this is a fully interactive visualization where each grid can be independently zoomed and panned and brush stroke highlighting in one view hightlights the covered points in the other view. Here the mouse is hovering over the upper left point in the volcano plot.\n\n![Hanami pic 6](resources/public/images/RNASeq-interactive-vis.png?raw=true)\n\n","funding_links":[],"categories":["Clojure"],"sub_categories":["[Tools](#tools-1)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsa-aerial%2Fhanami","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsa-aerial%2Fhanami","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsa-aerial%2Fhanami/lists"}