Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
david_stocker
Advisor
Advisor

This is part of a tutorial series on creating extension components for Design Studio.

Edit: This instalment has been updated to reflect a D3 best practice with regard to data handling.  See the last section.

So far, we’ve taken our first steps towards getting Eclipse set up for developing Design Studio components and we’ve taken a look at getting a component project set up.  If we’re going to build a full featured, dynamic, gauge component for Design Studio, we’re going to have to come to grips with drawing things in the browser; and we’re going to have to come to grips with it quite early.  Generally speaking, if you want to draw something in HTML5, there are two approaches; canvas and scalable vector graphics (SVG).

Canvas uses an eponymous html element.  You select your canvas element via ID and then use javascript to draw in it.  SVG also uses an eponymous html element.  You can either attach SVG drawings from external files, or draw in it, again, using Javascript.  SVG also uses the document’s document object model (DOM); with the drawing elements added to the DOM tree.  For the developer, the main difference is that canvas is pixel based and svg is vector based.  Use canvas when you have a lot of things to draw (and all the DOM nodes would bog the browser down) and don’t need to keep track of individual drawing elements.  Use svg when you do need to keep track of individual elements.

As gauges involve things that might turn, grow or shrink, being able to keep track of those things and manipulate them is handy.  Therefore, we’ll be using svg through this tutorial.  Since we’re using svg, we’ll probably want to use D3.js as our visualization manipulation library.  Firstly, D3 is the go-to library for manipulating svg in HTML5.  Secondly, it’s built into Design Studio’s SDK framework.

Step 2a.1 -

Let’s start by creating a barebones HTML document.  It won’t actually show anything when we open it in a browser, yet.  The document needs to have a header element and a body element.

The header needs to have a script element, with the reference to the D3 library.  This will import the D3 library when the document is opened in the browser and make it available.


  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>










In the body, we’ll add a div element and give it the id, ‘content’.

Also in the body, we’ll as a script element.  Leave it empty for now, as that will be where we write our code.

The HTML file should look like this:


<!DOCTYPE html>


<html>


  <head>


  <title>My First D3 Chart</title>


  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>


  </head>


  <body>


  <div id='content'></div>


  <script>




  </script>


  </body>


</html>










If you open it in a browser, should see a blank page.

Step 2a.2 –

Since gauges tend to be radial in nature, we're going to be working with arcs.  Therefore, we'll be frequently using Pi.  Fortunately, we don't need to define it ourselves, but we can instead use the constant maintained in JavaScript's Math library.  Add the following line of code:


  var pi = Math.PI;










Step 2a.3 –

We need attach an svg container to our content div.  To do this, we write the following:


var vis = d3.select("#content")


                    .append("svg:svg")


                    .attr("width", "100%")


                    .attr("height", "100%");










select() is a method on the D3 object.  It returns a selection.  The rest of the command acts on the thing selected in the first part.  This is called method chaining.

Tangential Exposition: Method Chaining  (skip this part if you already know how method chaining works)

In the code above, we wanted to do a few things:

We wanted to select the div, with the id of “content”.

Then, we wanted to append an svg element to the selection.

Then we wanted to set a couple of attributes on the selection.

In most languages – and indeed in vanilla Javascript as well, I’d need to write code that looked something like the following:


var vis = d3.select("#content");


vis.append("svg:svg")


vis.attr("width", "100%")


vis.attr("height", "100%");










Three of the four lines are single actions on the thing returned by the first.  The D3 library (and JQuery as well) adds a trick called method chaining.  This allows me to concatenate several steps into a single statement, as long as each new step is executing on the results of the last, or if the previous method did not return anything, whatever was still the last “target”.

In the case of the chained command set, above, it flows as follows:

  1. We start by executing the select method, on the d3 object.  The parameter in the selection is the ‘content’ tag.
  2. As select() returns a selection, this becomes the target, or this/self object for any chained method executed afterwards.  In this case, the selection is the div element with the tag ‘content’.
  3. This statement does have additional chained methods and the next in line is the append().  This will append an svg element to the content div.  The return value of the append() method is the newly added svg element.
  4. The two attr() methods set attributes on the svg element.

Step 2a.4 –

As svg is vector based, we'll be drawing lines using paths.  DashingD3 has an excellent overview of using D3 path generators.  In short, there are simple path generator commands for commonly used path primitives.  In this tutorial series, we'll be working mostly with lines and arcs.

Let's define a path for a simple arc:

  • It has an inner radius of 0 pixels.
  • It has an outer radius of 70 pixels.
  • It sweeps an angle from 45 degrees, to three radians*.


var arcDef = d3.svg.arc()


                        .innerRadius(0)


                        .outerRadius(70)


                        .startAngle(45 * (pi/180)) //converting from degs to radians


                        .endAngle(3) //just radians










*D3's paths actually use radians, so if you want to use degrees, you'll need to convert, as we did in the code snippet, above.

Step 2a.5 –

Now we're ready to actually draw an arc in our svg element:

  • We need to give it a fixed, pixel based height and width, or it won't display.  If anyone knows why D3 is so picky about this, I'd live to hear about it in the comments.
  • We need to append a path element to our svg element.
  • By default, the path will be black, but svg path elements have a number of style attributes, allowing us to define how it looks.  (Note - CSS can also be used to style svg elements)  Let's set the "fill" attribute to "red".  You can read more about style attributes on svg here.
  • By default, the origin (the 0,0 coordinate) of the arc is at the 0,0 coordinate of the svg element.  Since the arc will be drawn around the origin, we'd like it to be visible.  Let's offset the origin of the arc to the middle of the 400x400 pixel canvas that we define above.
  • The newly defined path element will need instructions on what path it should follow.  This is defined in the "d" attribute.  You can read more about how this attribute works on this page of the Mozilla developer docs, but the tl;dr version is that we can use the arcDef variable, which we defined above.  It is a valid path definition string.

var guageArc = vis


                        .attr("width", "400").attr("height", "400") // Added height and width so arc is visible


                        .append("path")


                        .style("fill", "red")


                        .attr("transform", "translate(200,200)")


                        .attr("d", arcDef);










Step 2a.6 –

Our completed html file should now look as follows:


  <!DOCTYPE html>


  <html>


      <head>


          <title>My First D3 Chart</title>


          <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>


      </head>


      <body>


          <div id='content'></div>


          <script>


                  var vis = d3.select("#content")


                                     .append("svg:svg")


                                     .attr("width", "100%")


                                     .attr("height", "100%");


                                 


                  var pi = Math.PI;



                  var arcDef = d3.svg.arc()


                                                  .innerRadius(0)


                                                  .outerRadius(70)


                                                  .startAngle(45 * (pi/180)) //converting from degs to radians


                                                  .endAngle(3) //just radians



                  var guageArc = vis


                                          .attr("width", "400").attr("height", "400") // Added height and width so arc is visible


                                          .append("path")


                                          .style("fill", "red")


                                          .attr("transform", "translate(200,200)")


                                          .attr("d", arcDef);


          </script>


      </body>


  </html>










Step 2a.7 –

When you run it, you should see a red pie fragment:

As always, the completed extension (as of part 2) is available as a Github repository.

Note: D3 is Data Driven!

I'd like to point something out here.  In the above code, we defined starAngle and endAngle for the arc directly.  This is *not* a D3 best practice.  If you are learning D3 along with Design Studio extension creation, then you may want to take a side trip to Part 10a, where we discuss using data, instead of directly defining properties.  For the time being (until part 10), we'll stick with the "wrong" way, that we defined above.  It will work just fine, though we'll have to make the change when delving into animating our gauge in part 10.

Next time, we'll get our arc into a Design Studio extension!

2 Comments