Giter VIP home page Giter VIP logo

locuszoom's Introduction

LocusZoom

LocusZoom is a Javascript/d3 embeddable plugin for interactively visualizing statistical genetic data from customizable sources.

For more information, see our paper:

Boughton, A. P. et al. LocusZoom.js: interactive and embeddable visualization of genetic association study results. Bioinformatics (2021) doi:10.1093/bioinformatics/btab186.

This is a low level library aimed at developers who want to customize their own data sharing/visualization tools. If you are a genetics researcher who just wants to make a fast visualization of your research results, try our user-friendly plot-your-own data services built on LocusZoom.js: my.locuszoom.org and LocalZoom.

Build Status

See https://statgen.github.io/locuszoom/docs/ for full documentation and API reference.

To see functional examples of plots generated with LocusZoom.js see statgen.github.io/locuszoom and statgen.github.io/locuszoom/#examples.

LocusZoom.js Standard Association Plot

Making a LocusZoom Plot: Quickstart tutorial

1. Include Necessary JavaScript and CSS

The page you build that embeds the LocusZoom plugin must include the following resources, found in the dist directory (or preferably loaded via CDN):

  • d3.js
    D3.js v5.16.0 is used to draw graphics in LocusZoom plots. It may be loaded via a CDN. It must be present before LocusZoom is loaded.

  • locuszoom.app.min.js
    This is the primary application logic. It should only be included after the vendor dependencies have been included.

  • locuszoom.css
    This is the primary stylesheet. It is namespaced so as not to conflict with any other styles defined on the same page.

Instead of copying the files to your project, we recommend using CDN links are for these resources (see statgen.github.io/locuszoom/).

The above instructions describe using LocusZoom with pure JS and HTML. If you are using a module build system, LocusZoom supports usage via ES6 imports, eg:

import LocusZoom from 'locuszoom';
import 'locuszoom/dist/locuszoom.css';

2. Define Data Sources

Data Sources is an object representing a collection of arbitrarily many sources from which data for the plot can be requested. When adding sources to the collection they must be namespaced so that retrieving specific fields can be done with respect to specific data sources.

Here's an example of defining a data sources object for a remote API:

var data_sources = new LocusZoom.DataSources();
data_sources.add("assoc", ["AssociationLZ", { url: "http://server.com/api/", source: 1 }]);

The above example adds an "AssociationLZ" data source (a predefined data source designed to make requests for association data) with a defined URL. The namespace for this data source is "assoc".

Data sources can also be local files:

data_sources = new LocusZoom.DataSources();
data_sources.add("assoc", ["AssociationLZ", { url: "file:///path/to/data.json" }]);

Refer to the Working with data guide for more information on using predefined data sources or extending/creating custom data sources.

3. Define a Layout

Layout is a serializable object that describes the configuration of the LocusZoom plot, including what data will be pulled from the data sources and displayed in what way, along with visual characteristics like color and geometry.

A layout definition may look something like this (simplified example; consult docs for details):

var layout = {
  width: 500,
  height: 500,
  panels: [
    {
      id: "association",
      data_layers: [
         {
           id: "association",
           type: "scatter",
           x_axis: { field: "assoc:position" },
           y_axis: { field: "assoc:pvalue" }
         }
      ]
    }
  ]
};

The above example defines a basic plot that is 500 pixels on a side and has one panel with one scatter plot data layer that pulls in position and pvalue from the "trait" data source, mapping position to the x axis and pvalue to the y axis.

The LocusZoom.js library provides several pre-defined layouts for entire plots and subdivisions of plots such as panels, data layers, tool tips, etc. Refer to the Layouts and visualization options guide for more information.

4. Put it Together with LocusZoom.populate()

With includes included, data sources defined, and a layout defined, LocusZoom.populate() will accept a CSS selector string to populate the first matching element with a plot.

A basic example may then look like this:

<html>
  <head>
    <script src="dist/locuszoom.app.min.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="dist/locuszoom.css"/>
  </head>
  <body>
    <div id="lz-plot"></div>
    <script type="text/javascript">
      const data_sources = new LocusZoom.DataSources();
      data_sources.add("assoc", ["AssociationLZ", { url: "https://server.com/api/single/", source: 1 }]);
      const layout = {
        width: 800,
        panels: [
          {
            id : "association",
            height: 300,
            data_layers: [
              {
                id: "association",
                type: "scatter",
                x_axis: { field: "assoc:position" },
                y_axis: { field: "assoc:log_pvalue" }
              }
            ]
          }
        ]
      };
      const plot = LocusZoom.populate("#lz-plot", data_sources, layout);
    </script>
  </body>
</html>

Other Ways To Make a LocusZoom Plot

Use a Predefined Layout

The core LocusZoom library comes equipped with several predefined layouts, organized by type ("plot", "panel", "data_layer", and "toolbar"). You can see what layouts are predefined by reading the documentation or introspecting in the browser by entering LocusZoom.Layouts.list() (or to list one specific type, like "data_layer": LocusZoom.Layouts.list(type)).

Get any predefined layout by type and name using LocusZoom.Layouts.get(type, name).

If your data matches the field names and formats of the UMich PortalDev API, these layouts will provide a quick way to get started. If your data obeys different format rules, customization may be necessary. (for example, some LocusZoom features assume the presence of a field called log_pvalue)

See the guide to working with layouts for further details.

Build a Layout Using Some Predefined Pieces

LocusZoom.Layouts.get(type, name) can also be used to pull predefined layouts of smaller pieces, like data layers or toolbars, into a custom layout:

const layout = {
  width: 1000,
  height: 500,
  panels: [
    LocusZoom.Layouts.get("panel", "association"),
    {
      id: "custom_panel",
      ...options
    },
    LocusZoom.Layouts.get("panel", "genes")
  ],
  ...
};

Modify a Predefined Layout

The get() function also accepts a partial layout to be merged with the predefined layout as a third argument, providing the ability to use predefined layouts as starting points for custom layouts with only minor differences. Example:

const overrides = { label_font_size: 20 };
LocusZoom.Layouts.get("data_layer", "genes", overrides);

Predefining State by Building a State Object

State is JSON-serializable object containing information that can affect the entire plot (including all data retrieval requests). State can be set before or after the plot is initialized. For example, the following special-named fields will cause the plot to be loaded to a specific region of interest on first render:

const layout = LocusZoom.Layouts.get('plot', 'standard_association', { state: { chr: 6, start: 20379709, end: 20979709 } })

Alternate: setting the initial view via data-region

You can also describe the locususing a data-region attribute of the containing element before populating it, like so:

<div id="lz-plot" data-region="10:114550452-115067678"></div>

When LocusZoom.populate() is executed on the element defined above it will automatically parse any data-region parameter to convert those values into the initial state.

Development Setup

Dependencies

LocusZoom is an entirely client-side library designed to plug into arbitrary data sets, be they local files, APIs, or something else entirely. It has the following external dependencies:

  • d3 for data visualization

Build System and Automated Testing

LocusZoom is bundled using Webpack. To install all necessary dependencies for a development environment, run:

$ npm install

We recommend using node.js v12 or greater to build the library and run tests.

Once complete run npm run build from the top of the application directory to run all tests and build the LocusZoom library bundle.

This build process will also write sourcemaps, to help with debugging code even in production environments.

Other supported build commands:

  • npm run test - Run unit tests (optional: npm run test:coverage to output a code coverage report)
  • npm run dev - Automatically rebuild the library whenever code changes (development mode)
  • npm run build - Run tests, and if they pass, build the library for release
  • npm run css - Rebuild the CSS using SASS (CSS rarely changes, so this doesn't get done automatically in dev mode)
  • npm run docs - Build just the library documentation
  • npm run format - Format the JavaScript code using ESLint

Automated Testing

LocusZoom uses Mocha for unit testing. Tests are located in the test subdirectory. Use npm run test.

Static analysis and code style

LocusZoom runs code quality checks via ESLint, the rules for which can be found in .eslintrc. This will run automatically as part of all new code commits, and during every build.

Help and Support

Full API documentation and prose guides are available at: https://statgen.github.io/locuszoom/docs/

A LocusZoom discussion forum is available here: https://groups.google.com/forum/#!forum/locuszoom. For the most effective help, please specify that your question is about "LocusZoom.js".

If you have questions or feedback please file an issue on the LocusZoom.js GitHub repository or post at the discussion forum referenced above.

locuszoom's People

Contributors

abought avatar dependabot[bot] avatar frencil avatar lrowe avatar mrflick avatar pjvandehaar avatar sir4ur0n avatar welchr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

locuszoom's Issues

Refactor existing data sources to allow for flexible field definitions

The Gene source currently implements a method whereby the data source doesn't care what fields are present in the response, allowing for much greater flexibility in the upstream data provider for that source. Other sources, especially the association source, could benefit greatly from this flexibility as an optional alternative to defining expected fields up front.

Expand template markup to allow for conditional blocks

As described by @pjvandehaar it would sometimes be useful, when available fields may vary from element to element in a single data layer, to write the general tool tip markup such that certain sections would only be displayed if the field is present on the element.

Note that handlebars.js, which was the inspiration for the double-curly markup notation in LocusZoom templates, does have a convention for conditionals, among other things.

Mousewheel zooming on data layers

* Depends on #15 and #16*

When a user mouses over a data layer and scrolls with their mouse wheel the data layers should "zoom" by changing the x range of the visible data set.

Based on a previous discussion with Broad developers the desired default behavior would be to scale the y range on the positions panel to fit all data regardless of the previous view's y range.

Estimate

This feature will probably take two weeks of development to build and test.

Methods to view/export underlying data

From the next round of goals for the T2D Portal:

Enhance LocusZoom to support tabular views of data for integration with the other new tables on the gene page

Groundwork for this could be API methods to provide data on a per-data-layer basis is JSON and/or CSV. These methods will need to implement sanitization logic to ensure JSON is clean and CSV is coherent.

That alone may be sufficient for the T2D Portal, but it may also be worthwhile to build a dashboard component to provide UI for those API hooks to enable raw data downloads.

Provide methods for clearing selections

As seen in the conditional analysis proof of concept in the Broad portal implementation of LocusZoom, there may be situations where an implementer may want to programmatically clear selections. Presently this is possible but only through direct manipulation of the state, and important deselection logic therefore doesn't necessarily get triggered.

The LocusZoom core library should extend methods to clear all selected elements on a per-plot, per-panel, and per-data-layer basis.

plot with values not log10 transformation

LocusZoom is really a good tool to plot regional genome.

And I tried to plot with other values but P-values, which is expected to plot with the direct values rather than the -log10 transformation values.

How LocusZoom do this?

Thank you in advance.

Safer method for registering custom DataLayers

The Problem

As of version 0.3.0 (see #30) DataLayer classes are checked for existence via basic string comparison, implying that custom DataLayer classes need to be directly added to the LocusZoom singleton. This could create conflicts in the future as implementers of the plugin overwrite existing attributes of the singleton.

Proposed Solution

As described by @mflick here this could be more effectively handled with a custom collection class here with add/get/set methods pattern as seen elsewhere in the plugin.

UI to show / edit variants used in conditional analysis

A working approach to single-variant conditional analysis is demonstrated in the current T2D portal by way of a button to condition on a variant. Once conditioned the variant disappears from the panel and there is no indication that a conditional analysis is applied, or with respect to which variant.

Multiple variant analysis is a desirable workflow. LocusZoom needs proper UI for displaying and editing conditional analysis selections.

Should dashboard buttons allow html rather than just text?

Right now,

  • component.text is plain text. I'd like to use HTML for glyphicons but don't need it.
  • component.title is plain text
  • title_component.title is html
  • menu_component.button_html is plain text
  • menu_component.menu_html is html
  • covariates_component.button_html is plain text

Should all of these allow arbitrary HTML?

Mock data sources for testing

The Problem

Presently we aren't able to effectively build automated tests for working with the Data object without pointing to a functioning data endpoint - something automated tests shouldn't have to rely on. Some sort of mock data end point is needed.

Deferred Transformations

Presently transformations are passed along from the fields array to the data source and applied to all elements as data is requested. While sometimes this may be necessary it can be a heavy-handed approach when, for example, just wanting a transformation applied for display (e.g. in a tooltip).

A solution would be to make it so that referencing data fields pretty much anywhere in the library (including retrieving data form sources, parsing tool tip markup, etc) the presence of a transformation is detected and applied as-needed, as opposed to treating the field+transformation as a distinct field that should just exist up front.

Document how to build custom Data Sources with different URL structures

The Problem

Presently data sources are implicitly assumed to be API endpoints that can only be accessed via an HTTP request with a rather specific URL structure. As LocusZoom is considered a candidate for pulling from other data sources we may need to support more generic methods for defining request URL composition.

@pjvandehaar - please comment here about the URL structure for data requests in Bravo, or wherever you may be considering plugging in LocusZoom. Seeing how those URLs must be composed, compared to how LocusZoom is currently built, will help demonstrate more generic methods.

Make instance resizing automatic, manual, or disabled

Presently by default all instances are manually resizable (with the resize_handle UI element).

At a minimum this should be a parameter that can be disabled, forcing an instance to keep a strictly defined size. Ideally a layout could also set this value to be something like "automatic" such that the instance always takes up 100% of its containing div.

Layout Builder Demo Page

Presently we have two demo pages in the repo to demonstrate setting up a LocusZoom plugin with only a slight variation in the layout. Moving forward we're going to be supporting an increasing amount of flexibility in the layout with only one configuration baked into LocusZoom.DefaultLayout, relegating all that flexibility to only the API Reference.

By building a dynamic demo page that has form elements to tweak the layout (which evolves with the library) we'll not only have a tool to demonstrate the level of variation LocusZoom affords developers but also have something we can stand up on locuszoom.sph.umich.edu to highlight current LocusZoom library development.

A minimum viable product might include:

  • A couple of big text areas showing the current layout and state objects
  • Form controls to submit any changes to the layout/state and apply them to the plot
  • A link to the API reference

It may be wise to avoid creating a big dynamic HTML form without first figuring out a way to model all layout possibilities in some sort of portable data structure which the library can also use for layout verification. Otherwise all new layout rules will need to be implemented twice and there will be a high risk of divergence between the library and the layout builder demo.

Make layouts partially definable

The Problem

Starting in version 0.3.0 (see pull request #30) layouts are serializable objects passed to a populate function. If no layout is passed then the DefaultLayout is used. However this is mostly binary... it is not possible to define only a few elements of a layout and have them gracefully merged with the default layout.

Example: Populate a LocusZoom instance as follows:

LocusZoom.populate("#div_id", datasources, { width: 1000 });

Ideally this would create a layout with all default values in place except override the width to be 1000 (instead of the currently defined default of 700).

Add Code Coverage Reports

It would be nice to get code coverage reports for our automated tests, and possibly also have those reports consumed by a service like Coveralls to track our coverage level over time.

Early attempts to hook up the gulp/mocha build/test process to some coverage reporting today proved less than fruitful. Istanbul is a viable node package for coverage reports but I could only get it to work command line separately (read: not in gulp context) and even then our coverage levels were dinged since dependencies (d3 and Q) were being counted against our totals. Will revisit in the future.

Support local file Data Sources

The Problem

Presently LocusZoom does not support creating a data source that maps to a local file. This is a likely use case for the plugin on a longer time scale.

It's unclear exactly how to approach this problem without having some example files to work with. The HTML5 File API will likely play a part.

Opportunities

In scope for this feature we may want to consider generating a few small "example" data files that live in the repository. These would be very useful for building automated tests against local file data sources logic (see #36, related).

From there we could also strike the current data sources from the demo and define a proper DefaultDataSources object (in keeping with the pattern of DefaultLayout and DefaultState). In this way the "hello world" for the LocusZoom plugin could literally be a one-liner (that is, one line of JavaScript logic to call populate and the HTML to include the scripts and create an empty div).

firefox issue and suggestions

  1. "Press [SHIFT] while scrolling to zoom" does work on Firefox v 50.0.2.
  2. It would be great if you include functions like Navigate buttons, search by gene name box and go to region box in examples.

Evaluate d3 v4.x and potentially upgrade

In June 2016 a rare major release for d3 dropped: version 4.x. Just about every corner of the library was touched in some way.

There may be useful new features and/or a performance benefit to upgrading. Or it could be a massive time sink that doesn't yield any appreciable benefit. Either way, given the scale of the changes between d3 3.x and 4.x and LocusZoom's current use of d3 as a core dependency, it should be evaluated.

Relative panel sizing

Depends on 15 and #19

With recursive redraw methods (#15) and resizable instances (#19) we can look at making panels themselves relatively sizable. The goal here is to extend UI elements that show panel boundaries on mouse over which intuitively appear clickable/draggable. Clicking/dragging a panel boundary should change the relative sizes of the adjacent panels.

For instance, this feature would allow for grabbing the "boundary" between the positions and genes panels and drag up to make the positions panel shorter / genes panel taller or drag down to do the opposite. Data within the panels should scale gracefully to changing available heights. This behavior should not effect the size of the containing instance, and a minimum panel height should be enforced.

Estimate

This feature will probably take about two weeks of development to build and test.

Plot-level dashboard component button menus can have wrong y-positioning

As demonstrated on the T2D portal, when a LocusZoom instance is loaded inside an expandable DOM element the position calculation for a plot-level dashboard component button menu (e.g. the model covariates menu) can appear way off. In the case of the T2D portal the LZ instance is in an expanding element with a height many times the height of the page, rendering the LZ instance at a decent scroll height down the page, but the button menu appears very close to the top of the page.

Ability to load dynamically-built URL for onclick action

Presently the behaviors system allows for determining the status a data element takes on click / shift-click / ctrl-click etc. However in some use cases where LocusZoom is an element of a larger platform (e.g. PheWeb) a more desirable behavior would be to tie data element click events to loading a new URL, ideally defined using the same templating markup used in tool tips to allow for dynamic URLs that draw from arbitrary fields on the element.

Export / PDF Generation

A potential use case for LocusZoom in the future is means of exporting a LocusZoom instance to a graphic or PDF suitable for inclusion in a paper.

This feature is arguably out of scope of the plugin itself and will require a companion repository for server-side installation.

Estimate

This feature will probably take at least a month of development to build and test because it branches out into a pretty different technology stack from the plugin itself.

Return Partial Data

If a data layer is requesting data from multiple sources and only one of those sources fails, try to find a way to still display what data was received.

A good example of where this would help: Positions data layer that pulls down positions from one source and LD from another. Since the LD data is only used to color positions, and the coloring function will return a default color if passed an undefined value, ideally it would be nice to still see the default-colored positions data if the LD source is not responding.

In practice this may be very difficult to pull off since the data layer makes one request and LocusZoom.Data does the logic to generate actual requests to each source necessary given the fields requested. Right now it's an all-or-none type thing, where either all data is returned or an error is passed through. To return partial data means potentially returning both data and errors in some cases, which does not have clearly defined behavior.

Adding `onUpdate` function causes `undefined` to be added `onUpdateFunctions`

Observed this behavior today: I created a function f() {} (literally did nothing), then called locusZoomPlot.onUpdate(f). That worked fine; however, the second call to onUpdate() (without argument, so therefore intending to run all of the stored functions) would yield a Uncaught TypeError: this.onUpdateFunctions[func] is not a function.

I replaced the onUpdate function with the following:

LocusZoom.Instance.prototype.onUpdate = function(func){
    debugger;
    console.log('this called');
    console.log('func is', func, typeof func == 'undefined'); 
    console.log('currently', this.onUpdateFunctions);
    if (typeof func == "undefined" && this.onUpdateFunctions.length){
        console.log('these conditions met');
        for (func in this.onUpdateFunctions){
            console.log('func iterator', func);
            this.onUpdateFunctions[func]();
            console.log('in iterator', this.onUpdateFunctions);
        }
    } else if (typeof func == "function") {
        this.onUpdateFunctions.push(func);
    }
    console.log('now', this.onUpdateFunctions);
};

Using this, I could see that f got added correctly. However, when onUpdate() was called the first time, this.onUpdateFunctions became [function function, undefined: undefined]. Further inspection showed that when onUpdateFunctions was called the first time without argument, it would iterate over the following values: 0, shuffle, swap, heapSort. 0 makes sense, as an array indice. The last 3 are surprising--this.onUpdateFunctions["shuffle"]() and this.onUpdateFunctions["heapSort]()" don't seem to do anything, but this.onUpdateFunctions["swap"]() seems to be adding the undefined to onUpdateFunctions. Not entirely sure where these are coming from--I assume it has something to do with the prototype methods for arrays in javascript, but some quick research didn't seem to show anything.

The fix seems to be swapping out the iteration like so:

this.onUpdateFunctions.forEach(function(funcToRun) {
     funcToRun();
});

I'll make up a PR here in a minute.

tl;dr: the way JS handles iteration over an array doesn't always seem to work correctly, which results in undefined getting added to onUpdateFunctions

Make tooltips not-interactable until `selected`

(Problem first tracked at statgen/pheweb#39 and solved for PheWeb but not LZ in general)

Unless a tooltip is selected, it should not be interactable. This is important when a label drifts behind where its tooltip will spawn, so that when you hover the label, the tooltip pops up under the mouse, removing the hover of the label, destroying the tooltip, repeat each time the mouse moves by a pixel.

I would like for LZ to apply this style to all non-selected tooltips:

.lz-data_layer-tooltip { pointer-events: none !important; }

(as recommended here and tested in Chrome/Safari/Firefox)

Currently PheWeb just applies that style to all tooltips, because none are ever interactable.

Dynamic Legend

Original LocusZoom would generate a legend depicting the numerical binning for colors on the positions plot.

This LocusZoom needs a mechanism to generate a legend. Most of the information that would generate a simple color legend is already defined in the layout, provided that the legend's size and position be hard coded or dynamically generated with respect to existing layout values. Regardless, at the very least, displaying a legend should be a boolean in the layout.

Legends may need to be data-layer-specific. For example, a scatter data layer legend may show values related to the color, shape, and even size of points on the plot. However a line data layer (which hasn't been built yet) would potentially show values related to the color, style (solid/dotted/dashed), or area beneath a line.

Credible sets

We had a request from Andrew Morris to see if its feasible to display the credible set of variants for a particular association peak.

This feature request would involve two parts

  • A data source (or transformation) to do the calculation based on the p-values (or test statistics) of all SNPs in a region. Each SNP would get a dichotomous label: in or out of the credible set.
  • A way to visualize the SNPs in the credible set. This could either be some transformation on the p-value points or a separate annotation track.

API methods to support removing/reordering data layers within a panel

While the API methods and UI elements for programatically adding, removing, and rearranging panels on a populated LocusZoom plot are fairly mature at this point, analogous methods for data layers are not.

Presently it is possible to programatically add a data layer with Panel.addDataLayer(). That's about it.

What's needed:

  • Panel.removeDataLayer()
  • Method(s) to alter the relative z indexes of data layers in a panel and "apply" it (i.e. reorder data layer elements in the SVG to achieve the desired visual result)
  • UI elements analogous to the panel controls elements to allow for changing the z-index order of data layers in a panel interactively

Make layouts more live-editable

The Problem

Starting in version 0.3.0 (see pull request #30) layouts are serializable objects passed to a populate function. Once a layout is defined for an object, however, it's more or less static. A handful of methods such as setDimensons() and setMargin() on Instance and Panel or setOrigin() on Panel provide means for editing a current layout, but these are inconsistent and not general enough to apply to any arbitrary attribute of a layout.

Ideally an API should be available to allow an end user to safely modify any attribute of an existing layout and see that change immediately take effect.

Mouse guides

The Problem

Vertical alignment of information is a critical piece of the data LocusZoom displays, but "eyeballing" which things are actually lining up can be difficult.

A Solution

The obvious approach is to create a vertical "guide" which would be little more than a vertical line that follows the mouse. A horizontal guide would be just as easy to add.

The trick here, though, is where in the arrangement of the SVG object this should appear. Arguably it should appear behind data since any rendered data should have the potential to be interactive, and if a guide is rendering in front then it would capture mouse over and click events instead of the elements rendered below it.

Smarter responsive resizing modes

This is the continuation of a discussion started in #41 with @MrFlick and @benralexander.

Presently resizing can be none, manual, or responsive, which keeps a fixed aspect ratio.

It has been suggested that responsive could manifest in a few different ways, depending on the end use case. For example:

  • Keep the plot height constant but allow for aspect ratio to adjust as the container adjusts (i.e. responsive only in the x dimension)
  • Have a more dynamic aspect ratio that mimics that of the page itself. This would be useful in the case of a tablet or phone displaying a plot and changing between portrait and landscape.

Are there other "responsive" modes we can think of? Both of these would be straightforward to implement. New layout syntax would be required.

Make mouse guides toggle-able via layout

As documented in this issue, sometimes mouse guides aren't wanted. They should still be on by default, but should be hooked to a layout directive (at the plot level) that can be set to false to prevent them from rendering.

Conditional tags evaluate 0 as true, which may be an unexpected corner case

In #101 the {{#if foo}}{{/if}} conditional was changed such that the value (foo) is evaluated as true when 0. This solves the immediate problem that arises when using the conditional tag for determining if a field is populated with any value but may break expected behavior should a value of 0 need to be evaluated as false.

A potential solution would be to expand the conditional tag to optionally support an operator and comparison value, allowing for more explicitly defined behavior.

Resizable instances

Depends on #15

Once a recursive redraw methods pattern is in place (#15) we can look at making instances themselves resizable. The ideal behavior here would be a standard-looking "resize" handle appearing in the lower right corner with click/drag behavior that resizes the entire SVG element and all panels/data therein proportionally.

Note that the CSS3 resize property won't help us here. This works well for HTML elements but is not implemented for SVG. We'll need to implement a UI layer for showing the resize handle we draw ourselves, and use mouse event listeners to implement the resize behavior.

Estimate

This feature will probably take about a week of development to build and test.

Support POST Requests

Presently LocusZoom is hard-coded to only submit GET requests to upstream APIs for data.

We have our first use case of an API that will only accept POST requests. Specifically: the ExAC API for getting gene constraint data on arbitrarily many genes can accept an array of gene IDs (e.g. all genes returned to the genes panel for the region we're looking at) but will only work with a POST request.

@MrFlick - pulling gene constraint data from the ExAC API for gene tool tips in the Broad portal's LZ implementation is on the to-do list for ADA, so this has a high priority. Please let me know what you think about the prospect of this feature and how best to get a minimum viable product for it into production LocusZoom. If you're too swamped to look at this before ADA I can potentially put something together but wouldn't presume to start without at least talking through the theory behind it with you first.

Help for user data

Hello:

Is there any way to use the custom GWAS/eQTL data in locuszoom.js ? And how to add it as the datasource (data are txt file and without api support)? Thanks.

Structure panels and data layers as arrays in the layout

As described by @jonathonl, the current convention for defining lists of primary objects in the layout (panels and data layers) is confusing:

var layout = {
  ...
  panels: {
    panel_1_id: {
      ...
      data_layers: {
        data_layer_1_id: {
          ...
        }
      }
    },
    panel_2_id: {
      ...
    }
  }
};

The problem here is that panel and data layer IDs are expressed as keys in a map, a space generally reserved for supported/documented attribute names, not user-defined values. A more intuitive approach would make the id value a named parameter and use arrays, like so:

var layout = {
  ...
  panels: [
    {
      id: "panel_1_id",
      data_layers: [
        {
          id: "data_layer_1_id",
          ...
        }
      ]
    },
    {
      id: "panel_2_id",
      ...
    }
  ]
};

While this is a pretty significant breaking change it makes the layout much more internally consistent.

UI for reordering Genes

Depends on #15

The problem

Genes are displayed on the Genes panel in the order they come back from the gene data API. Rudimentary stacking logic makes sure genes don't overlap, but there's no context for elevating "more important" genes to the top of the data layer.

The solution

In lieu of the API itself somehow returning genes in order of importance the most elegant and immediate solution is to allow a user to reorder genes on the panel themselves through an intuitive click+drag UI. Mousing over a gene should "highlight" it by showing its bounding region. Clicking and dragging the gene should allow it to jump vertically between "tracks" but it should not be able to move horizontally.

As a gene jumps to another track any genes it overlaps with should be bumped to either of the two adjacent tracks. Logic for choosing the right direction based on the situation may take some iteration to get right. This is probably most effectively done with a recursive method for bumping genes that also adds tracks in case more tracks are needed to support the new arrangement.

This creates a tricky expected behavior where a new track may be added outside of the available draw area for the panel. Options for addressing this problem:

  • Scale the height of all gene tracks proportionally to the height of the data layer. If a new gene track is added during a drag event all gene tracks are scaled slightly smaller to accommodate the new track. (this seems ideal...)
  • Resize the entire instance in order to also resize the genes panel to allow for new tracks to be added without changing the height of existing tracks. (this seems weird and may be counter-intuitive to users)

Estimate

This feature will probably take at least three weeks of development to build and test. In particular, capturing the expected behavior of genes reordering themselves will probably be pretty challenging and present a lot of edge cases.

Pattern for customizeable floating data info/UI boxes

Existing visualizations allow for displaying only a subset of the information potentially returned from APIs. There's also no pattern for extending interactive elements to individual data points, such as recasting LD coloring for data on the positions panel based on a selected point.

We should try to do this in the most general way possible but will need to preprogram our default instance to meet the needs of a specific use case (the Broad portal) to start. Overlaid info boxes should be built in HTML, not SVG, purely for the ease of working with text, links, and tabular data within them (example here).

Estimate

This feature will probably take about two weeks of development to build and test.

Dependencies

This depends on the completion of #30 which establishes a new serializable layout syntax. That syntax will allow for much more intuitive optional inclusion and customization of arbitrary "HTML modules" such as this feature.

Drag panels horizontally to remap to new data (panning)

Depends on #15

When a user clicks and drags horizontally on a data layer's background the charts and axes should drag left/right, and when completing a drag event should trigger a remap event to render new data based on new regions now in view.

Based on a previous discussion with Broad developers the desired default behavior would be to scale the y range on the positions panel to fit all data regardless of the previous view's y range.

Estimate

This feature will probably take two weeks of development to build and test.

Allow scale functions to take arbitrarily many fields

See this issue for context. The use case here is that the point shape on a scatter plot (something that can already be mapped to scale functions) needs to do some logic that conditions on more than one field at a time, and presently scale functions only allow one field to be passed. A general solution would be for scale functions to take an array of field names, an entire data element, or both.

Recursive transitional re-draw methods

For two major features (dynamic resizing of instances/panels and panning/zooming) we need recursive tiers of transitional re-draw methods.

The problem

Essentially right now all svg elements are rendered once. There are API endpoints to change the sizes of elements or to map to a new section of data but in all cases existing elements must be dropped and drawn again from scratch.

The solution

We need similar methods on all nested objects (instance, panel, data layer) that assume all svg elements already exist and, using updated state data, define proper d3 transitions to smoothly animate the resizing/positioning of elements without destroying/recreating them.

Estimate

This feature will probably take a dedicated week of development to build and test.

Make onUpdate handler more granular

Presently implementers can register functions with a LocusZoom plot to be triggered "onUpdate", but this is a catch-all that fires whenever any aspect of the plot has updated (e.g. new data received and rendered, a single element selected, a resize has occurred). The single catch-all was initially useful for developing the plot builder demo, which implements an onUpdate handler to update a textarea showing the current layout.

The catch-all trigger for whenever anything has changed should remain, but a mechanism for registering custom functions to fire when only specific classes of plot events (like rendering new data) have occurred would be more immediately useful to implementers.

Update documentation to reflect new layout syntax

The Problem

Starting in version 0.3.0 (see pull request #30) layouts are serializable objects passed to a populate function. The documentation on this repository and the README need to be updated to reflect the new approach to defining and working with layouts, including all supported parameters.

Implement finer-grain visual "loading" indicators

Presently the only built-in option for loading is the curtain (plot-wide or panel-specific). While it has a customizable message its general appearance is otherwise set in stone. It is a blunt tool for showing loading progress, as seen with some recent implementations of tool-tip-fired applyState() calls that bring in new data but may take a few seconds to run.

Less jarring and more elegant visual loading indicators need to be explored. One suggestion was to emulate the thin loading bar convention as seen in nanobar.js.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.