Skip to main content
RSS feed Subscribe to feed

 

How to Create a Visualization Web View

To display a custom Spotfire visualization in the Web Player, implement a custom web control. It must be integrated with the model object of the visualization to communicate state change and receive updates.

Overview

This topic describes how to create a custom web visualization for Spotfire Web Player.

Background Information
  • Creating a Visualization
    A visualization is a visual displaying data in Spotfire. A custom visualization defines a unique visualization, implementing model, view, and possibly using additional components.
  • The Web Document
    The web document is a node tree on the server. When rendered, it translates the document model into a rendered web document that can be visualized in a web browser.
  • The Client Communication Engine
    The Client Communication Engine enbables the client side web control to send control signals to its server side. If the signal changes any web control, the server responds with one or more change objects. The Client Communication Engine updates the client UI accordingly.
Prerequisites
  • Spotfire SDK\Examples\Extensions\SpotfireDeveloper.CustomVisualsExample
    The project contains the code of several visualizations. This tutorial describes the SpotfireDeveloper.CustomScatterPlot custom visualization, but also outlines how to use the ChartFX for .NET third parthy visualization control.
  • To implement an interactive control, the AJAX framework is required. It is not provided by Spotfire.
    Custom AJAX request/response designates communication between the client and the server side custom ASP.NET control. The Spotfire framework does not know about these requests. The control must implement them.
    Some third-party controls, such as ChartFX for .NET, already have AJAX support for user interaction. When developing custom ASP.NET controls, use the AJAX framework of your own choosing.

Client Side API

On the client side the control is placed in an IFRAME element. An IFRAME has its own JavaScript global scope. Any JavaScript files needed for AJAX to work in the scope of the IFRAME can be included. Spotfire itself will include the CustomVisualization.js file in the IFRAME to integrate the control with Spotfire client framework. The file contains the following methods:

  • update()
    Called by the custom control to get an updated analysis from the server. Using the Client Communication Engine it polls for updates from the server.
  • onInvalidated()
    Invalided from the Client Communication Engine, the custom control must update itself.

Server Side API

The server API consists in the VisualControlState class. It holds the model node. Since the ASP.NET control is stateless, it also holds a state preserving the data related rendering of a control.

The custom visualization receives a VisualControlState instance in its constructor, as indicated by the Enabling a Third-Party Control in the Web Player code example below.

Client-Server API Interaction Example

The problems solved by the APIs are best understood from the interaction between web browser client and web server when the Web Player user opens an analysis:

  1. The client sends a request: Open the analysis.
  2. The server creates the web document and a CustomControlProxy.
    The CustomControlProxy is a web document node acting as a placeholder for the ASP.NET control in the web document:
    • It invalidates this node when the model's RenderTrigger fires.
    • Keep state data and model for the control in VisualControlState.
  3. The server renders the web document to HTML.
  4. The CustomControlProxy web document node renders itself as an IFRAME element.
    The SRC attribute of the IFRAME is set to the CustomVisualPage.aspx page:
                <iframe src="CustomVisualPage.aspx?waid=guid&nid=guid">
                
  5. The client requests the CustomVisualPage.aspx page from the server.
  6. The server creates the CustomVisualPage.aspx page.
    This stateless ASP.NET page is created for each request from the client. It displays the custom ASP.NET control.
  7. The CustomVisualPage.aspx page uses the ViewRegistry to create the right type of custom visualization ASP.NET control, and adds it to the page.
  8. The server renders the custom ASP.NET control and returns it in the response to the client.
  9. The custom ASP.NET control is disposed along with the CustomVisualPage.aspx page.

User Interaction Example: Marking from Action to Reaction

Assume that the custom plot has implemented a user interaction that affects the model in some way, for instance marking. Then the following steps describe the typical interaction flow.

Custom web visualization interaction process
  1. The user marks a part of the custom visualization on the client.
    A custom AJAX request is sent to the custom ASP.NET control on the server.
  2. The custom ASP.NET control receives the request. The selected area is marked in the model. The change in the model causes the web document to be invalidated.
  3. When the AJAX response is received on the client, update is called.
    This call tells the Client Communication Engine to start polling for updates from the server.
  4. The Client Communication Engine polls the server for updates.
  5. The server responds to Client Communication Engine polling by sending the updated web document nodes to the client.
  6. The Client Communication Engine evaluates the updates from the server.
    The CustomControlProxy script calls the onInvalidated method.
  7. When onInvalidated is called the client custom control sends an AJAX request to refresh the custom ASP.NET control.
  8. The server responds with an updated custom control.

Examples

The following examples detail common development scenarios for custom web visualization development: Enabling a third-party control in the Web Player and adding interaction to a custom ASP.NET control.

The web server is multi-threaded, but since the custom ASP.NET control executes on the main application thread, multi-threading is not at issue when developing custom web player controls.

Enabling a Third-Party Control in the Web Player

This example shows how to add Web Player support for a custom visualization based on a third-party component with its own AJAX based interaction, like the ChartFX package, which contains both a .NET forms control and an ASP.NET web control. To be able to view ChartFX in an analysis in Web Player, a web control inheriting from the ASP.NET web control is implemented. This is how web control is built.

In the constructor, load the current data into the ChartFX data model and set up the menus to add some custom interaction:

public ChartFxWebControl(VisualControlState<ChartFxVisual, object> state)
    : base()
        {
    ColumnIdCollection = new Dictionary<int, MenuChoice>();
    this.model = state.Node;
    this.AxisX.Title.Text = this.model.XAxis.Name;
    this.AxisY.Title.Text = this.model.YAxis.Name;
    this.Data.Series = 1;
    CustomizeMenus();
    NewData();
    this.RenderFormat = "Image";
}

Override OnLoad to include JavaScript defining callbacks for the interaction:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    string javascript = 
        "function refresh() {\n" +
        "  SFX_SendUserCallback('" + this.ID + "','refresh',false);\n" +
        "};\n" +

        "customVisualization.onInvalidated = refresh;\n" +

The resetFilters callback is connected to a custom menu item during menu setup:

        "function resetFilters() {\n" +
        "  SFX_onCallbackReadyDelegate = function (context, result) {\n" +
        "    customVisualization.update();\n" +
        "  };\n" +
        "  SFX_SendUserCallback('" + this.ID + "','resetFilters',false);\n" +
        "  SFX_ClearLayers();" +
        "};\n";

    this.Page.ClientScript.RegisterClientScriptBlock(typeof(ChartFxWebControl), 
        "ChartFxWebControl", javascript, true);

Hook in the callbacks: On refresh reload data into ChartFX:

protected override void OnUserCallback(UserCallbackEventArgs e)
{
    base.OnUserCallback(e);
    if (e.Param == "refresh")
    {
        NewData();
    }

On resetFilters reset the filters in the Spotfire document model:

    else if (e.Param == "resetFilters")
    {
        this.model.ResetAllFilters();
    }
}

Adding Interaction to a Custom ASP.NET Control

An ASP.NET control supporting interaction through AJAX can be used solve different tasks. The following examples are covered in the SDK:

  • The SpotfireDeveloper.CustomScatterPlot implements marking interaction.
    Custom scatter plot
  • The SpotfireDeveloper.WebDetails developer example, which displays a web page.

The following code snippets focus on the communication between the client and the server. The example contains a method to make AJAX calls to the server, XmlHttp.post. It takes four parameters: the URL, the parameter values to send to the server, and a callback function for the response. When the user has marked an area on the client, the marked rectangle is sent to the server with a call to . The callback function is set to customVisualization.update. Refer to the CustomScatterPlotWebControl.js file for the source code:

function serverMark(markMode, rectangle)
{
    var values = { mark: 'mark'}; 
    values.markMode = markMode;
    values.x = rectangle.x;
    values.y = rectangle.y;
    values.width = rectangle.width;
    values.height = rectangle.height;
    XmlHttp.post(window.location.href, values, customVisualization.update);
}

On the server the custom control receives the request and selects the markers with a call to the SelectMarkers method on the model:

if (Context.Request.Form["mark"] != null)
{                
    int x = int.Parse(Context.Request.Form["x"]);
    int y = int.Parse(Context.Request.Form["y"]);
    int width = int.Parse(Context.Request.Form["width"]);
    int height = int.Parse(Context.Request.Form["height"]);

    Layout layout = GetLayoutFromState();
    if (layout != null)
    {
        state.Node.MarkArea(
            new Rectangle(x, y, width, height),
            GetMarkMode(Context.Request.Form["markMode"]),
            layout.Plot);
    }
}

On the client the function onInvalidated is called when the visualization is invalid and should update. The client triggers a request for a new visualization image by setting the src attribute of the image tag to a new string, obtained by increaseImageId:

customVisualization.onInvalidated = updateImage;

// Request a new image from Server by setting image source.
function updateImage()
{
    var image = document.getElementById('visualizationImage');     
    image.src = increaseImageId(image.src);
}

The server side ASP.NET control generates a new image:

if (Context.Request.QueryString["type"] == "image")
{
    HttpContext context = this.Context;
    context.Response.Clear();
    context.Response.ContentType = "image/png";

    // Get a render result and save the image to a stream
    using (RenderResult renderResult = state.Node.Render(new Size((int)this.Width.Value, (int)this.Height.Value)))
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            renderResult.Image.Save(memoryStream, ImageFormat.Png);
            memoryStream.WriteTo(context.Response.OutputStream);
        }

        // Remember the layout. We will use it when marking
        state.SetValue(renderResult.Layout);
    }
   
    context.Response.End();
}