cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Widget Extensions User D3

sgreywilson
Pearl

Widget Extensions User D3

Initial Objective statements

This post is about getting D3 connected as an extension to Thingworx.

There are a number of existing extensions using D3 but I wanted to explore a simple use case to make it easier to get into and bring out 2 additional points

  • Using an infotable as data input
  • Resize

The output looks like the image below

and the data was generated by a Timer based random value generator that set the values on a Thing every minute.

The data into the Widget is from a core service QueryHistory (a wrapped service that uses QueryProperyHistory)

In this example I will use temp as the variable in focus

If you have never created an extension take a look at Widget Extensions Introduction which provides a start to understanding the steps defined below, which are the core points to keep it relatively short.


The extension will be called d3timeseries and will use the standard design pattern

Create a folder called d3timeseries and create a subfolder ui and a add a metadata.xml file to the d3timeseries

From there create the files and folder structure

define the metadata.xml using CDN url for

D3 url url="https://d3js.org/d3.v4.js"

legend url = "https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.3/d3-legend.js"

Also check out https://d3js.org/ which provides documentation and examples for D3

For the initial set of Properties that control the D3 will use

DataAsINFOTABLE (Data coming into d3)

Title

XLegendTitle

YLegendTitle

TopMargin

BottomMargin

LeftMargin

RightMargin

Note: we are not using Width and Height as in previous articles but setting 'supportsAutoResize': true,

Below shows the general structure will use for the d3timeseries.ide.js properties

After deploying the extension  (take look at Widget Extensions Introduction to understand the how) we can see its now possible to provide Data input and some layout controls as parameters

From there we can work in the d3timeseries.runtime.js file to define how to consume and pass data to D3.

There a 4 basic function that need to be defined

  • this.renderHtml
  • this.afterRender
  • this.updateProperty
  • this.resize

renderHtml

afterRender

updateProperty

resize

The actual D3 worker is drawChart which I will break down the highlights


I use an init function to setup where the SVG element will be placed



The init is called inside drawChart


Next inside drawChart the rowData incoming parameter is checked for any content we can consume the expected rows object

Next the x and y ranges need to be defined and notice that I have hardcoded for d.timestamp and d.temp these 2 are returned in the infotable rows

The last variable inputs are the layout properties

Now we have the general inputs defined the last piece is to use D3 to draw the visualization (and note we have chosen a simple visualization timeseries chart)

Define a svg variable and use D3 to select the div element defined in the init function. Also remove any existing elements this helps in the resize call.

Get the current width and height as before

Now do some D3 magic (You will have to read in more detail the D3 documentation to get the complete understanding)

Below sets up the x and y axis and labels

Next define x and y scale so the visualization fits in the area available and actually add the axis's and ticks, plus the definition for the actual line const line = d3.line()

Now we are ready for the row data which gets defined as data and passed to the xScale and yScale using in the const line = d3.line()

After zipping up and deploying and using in a mashup you should get a D3 timeseries chart.

Code for the QueryHistory

logger.debug("Calling "+ me.name + ":QueryHistory");

// result: INFOTABLE

var result = me.QueryPropertyHistory({

maxItems: undefined /* NUMBER */,

startDate: undefined /* DATETIME */,

endDate: undefined /* DATETIME */,

oldestFirst: undefined /* BOOLEAN */,

query: undefined /* QUERY */

});

Thing properties example

Random generator code

me.hum = Math.random() * 100;

me.temp = Math.random() * 100;

message = message + "Hum=" + me.hum+ " ";

message = message + "Temp=" +me.temp+ " ";

logger.debug(me.name + "  RandomGenerator values= " + message );

result = message;

Previous Posts

2 REPLIES 2

Re: Widget Extensions User D3

Hi @sgreywilson

Thank you for your post for helping me to get started on my widget extension using d3.js. However, rather than a timeseries chart, I now have an XY chart which looks as follows (done using the existing XY chart widget in Thingworx). I want the x-axis to be on the top of the chart so that the intersection of the x-axis with the y-axis is in the top left corner instead of the bottom left corner. The x-axis values should increase from left to right and the y-axis values should increase from top to bottom. Therefore, I have combined the code of an XY chart widget with your code.

Current results (using existing XY chart widget and d3.js respectively):

Capture1.PNG

Capture1.PNG

Expected result:

Capture (1).PNG

I have studied and followed your example but I am stuck on getting the grid lines for the x-axis and displaying the binded data in my infotable. My infotable is as shown below. I am using the XAxis value (1,2,3) for my XAxisField and ASensor value (5,10,15) for my DataField1 in the composer.

Capture1.PNG

I have attached the zip file containing the code for the existing XY chart widget for reference as well. Any help will be greatly appreciated!

Re: Widget Extensions User D3

I am currently using Thingworx 8.5.3. I am trying to integrate D3 charts using custom widget extension.

I have an fair idea how I need to set up environment using eclipse. Now I need an help how to proceed further with code devlopment.

 

Announcements

Check out the upcoming Expert Session: Understanding ThingWorx Navigate Licensing in Community "Customer Events" section.