Last time out we created the Hello World extension for SAP Lumira. If you have not created an SAP Lumira extension before I’d suggest reading that post first.

This time I will walk you through a more real world example showing how to bring in a D3 chart as extension for SAP Lumira, I will be using the D3 Bullet Chart as an example but the steps should be similar for other charts.

Updated for SAP Lumira 1.20 as there were improvements made for creating SAP Lumira  extensions since this was first written.

You will need to be comfortable with JavaScript and D3 to follow this guide as this is a more advanced example.

Step by step guide

What applications do you need?

You need to install:

  1. SAP Lumira 1.20 or later (which includes vizPacker), you can get your free Personal Edition here
  2. Google Chrome as vizPacker only works with Google Chrome

Prepare the D3 code for re-use

To save time I have already made these changes to a reusable local copy of the D3 Bullet Chart code (look for the // MDL comments), see the resources section (below), you can find the modified files on GitHub.

The Bullet Chart uses a simple JSON data structure, think of it being a JSON version of a CSV file.

[

  {“title”:”Revenue”,”subtitle”:”US$, in thousands”,”ranges”:[150,225,300],”measures”:[220,270],”markers”:[250]},

  {“title”:”Profit”,”subtitle”:”%”,”ranges”:[20,25,30],”measures”:[21,23],”markers”:[26]},

  {“title”:”Order Size”,”subtitle”:”US$, average”,”ranges”:[350,500,600],”measures”:[100,320],”markers”:[550]},

  {“title”:”New Customers”,”subtitle”:”count”,”ranges”:[1400,2000,2500],”measures”:[1000,1650],”markers”:[2100]},

  {“title”:”Satisfaction”,”subtitle”:”out of 5″,”ranges”:[3.5,4.25,5],”measures”:[3.2,4.7],”markers”:[4.4]}

]

Originally vizPacker used this JSON format which suited the D3 Bullet Chart but not many other D3 charts. The majority of D3 charts use D3.csv data format, so vizPacker now uses the D3.csv data format as well to make things easier.

We will see later on in this blog how to convert/map from the D3.csv format to the JSON format for use with the Bullet Chart.

At a high level here are the changes I made:

    1. Save a local copy of the bullet chart index.html, bullet.js and bullets.json files from: Bullet Charts
    2. Update index.html:
      • We are going to mimic the vizPacker behavior
      • Add an SVG tag as the container for the extension into the page body and set the id to vis

        <svg id=”vis”></svg>

        <script src=”http://d3js.org/d3.v3.min.js“></script>

      • Add the vis variable and assign it from the vis SVG tag in the top of the script – this is the variable the vizPacker render code uses:

        <script>

        var vis = d3.select(“#vis”);

      • Hard code the test JSON data in a variable called fdatathis is what the vizPacker render function does by default
        • See the following optional step
      • Optional step – change the fdata JSON property names to make them more user friendly when they are shown in Lumira:
        • Combine title and subtitle into one property called Titles
        • Change measures to Actuals
        • Change markers to Target
        • Change ranges to Ranges
        • Update the bullet chart code to use the user friendly property names:

          var vis = d3.select(“#vis”);

          // MDL: embed JSON so we can test with Google Chrome locally.

          // MDL: renamed “data” to “fdata”.

          // MDL: Combined “title” and “subtitle” as “Titles” array.

          // MDL: Renamed ranges to “Ranges”.

          // MDL: Renamed measures to “Actuals”.

          // MDL: Renamed markers to “Target”.

          var fdata = [

                {“Titles”:[“Revenue”,”US$, in thousands”],”Ranges”:[150,225,300],”Actuals”:[220,270],”Target”:[250]},

                {“Titles”:[“Profit”,”%”],”Ranges”:[20,25,30],”Actuals”:[21,23],”Target”:[26]},

                {“Titles”:[“Order Size”,”US$, average”],”Ranges”:[350,500,600],”Actuals”:[100,320],”Target”:[550]},

                {“Titles”:[“New Customers”,”count”],”Ranges”:[1400,2000,2500],”Actuals”:[1000,1650],”Target”:[2100]},

                {“Titles”:[“Satisfaction”,”out of 5″],”Ranges”:[3.5,4.25,5],”Actuals”:[3.2,4.7],”Target”:[4.4]}

              ];

          // MDL: end

      • Add a y position attribute that positions each bullet chart in the vis SVG tag
        • Note: The original bullet chart relied on a DIV container so it did not need the y position, it would automatically put the next bullet chart onto a new line in the web page, that does not happen when you are using SVG so you need to add the y position to get the same look
          y position.png
    • Remove the randomize button and code as that is not needed in Lumira
  1. Update bullet.js:
    • Add code so that the bullet chart can still draw when there are no Titles, Actuals, Ranges, or Target passed to it as that is what Lumira will do when you first create the chart
      • Calculate the max range even if there is no datadraw when no data.png
  2. Test the updated index.html file in Google Chrome:
    • To make sure it runs and visually looks the same as the original did

Create the D3 extension

Follow these steps to create and test your D3 Bullet Chart extension inside the vizPacker:

  1. Using Google Chrome
    Open: <installdir>\SAP Lumira\Desktop\utilities\vizPacker\vizPacker.html
  2. The LAYOUT DESIGN tab is where you define the ID, Name and Layout of your extension as well as edit the code
  3. Using the LAYOUT DESIGN tab, set the ID and name of your extension:
    • Click the outer gray box to display the chart visualization properties:
      Edit extension ID - outer gray box.png
    • About IDs:
      • The ID naming convention is similar to making unique Java class names
      • You use the reverse of your company web address and then add the name of the extension
        For example: com.sap.sample.d3.bulletchart
      • IMPORTANT note for SAP Lumira 1.15:
        • The ID must be all lowercase otherwise it will not work inside SAP Lumira
    • Settings:
      • ID:       com.sap.sample.d3.bulletchart
      • Name: Bullet Chart
    • Note: As you close the properties window or move between fields the vizPacker updates the ID and Name used in the code editor
  4. Using the LAYOUT DESIGN tab, set the ID and name of your extension plot area:
    • Click the inner gray box to display the chart plot are properties:
      Edit plot area ID - inner gray box.png
    • About IDs:
      • Follow the same rules as for the extension ID, but append module to the end
    • Settings:
      • ID:       com.sap.sample.d3.bulletchartmodule
      • Name: Bullet Chart Module
    • Note: As you close the properties window or move between fields the vizPacker updates the ID and Name used in the code editor
  5. Remove the legend as the Bullet Chart does not need a legend
    • Click the X at the top of Legend:
      remove legend.png
  6. Import test data for the Bullet Chart:
    • Create a CSV version of the JSON Bullet Chart data, see the resources section (below) for the CSV file I made for the Bullet Chart
    • Switch to the DATA MODEL tab
    • Click to select and upload the CSV file:
    • Click to upload a CSV file.png
    • After you have chosen your CSV file.
    • Click OK when you see a JavaScript alert as you import the CSV file:
    • The data model now shows the imported CSV data:
      Imported CSV data.png
    • Note: There are 8 dimensions (one for each column) and 0 measures
  7. Next we will create the measure and dimension groups (another names for arrays, as they will be used in Lumira) and map test data columns to them.
    • Columns Title and Subtile ===> Titles
    • Columns Target ===> Target
    • Columns Range 1, Range 2 and Range 3 ===> Ranges
  8. Measure and dimension groups:
    • Measure groups (arrays) are for the numeric values in Lumira
      • So for the Bullet Chart these are the Actuals, Target and Ranges
    • Dimension groups (arrays) are for everything else:
      • So for the Bullet Chart these are the Titles
  9. Create the measure and dimension groups to match the JSON format that we need:
    • Create Titles as the first dimension group:
      • Click the drop down arrow next to Title
      • Then the right arrow (if expanded will show a drop down arrow as below) for the dimension
        Dimension group 1.png
      • Double click the name X Axis (the name the first radio button) and change the name to Titles, then press ENTER
        Title dimension group 1.png
      • And select the first radio button, so the Title column is now mapped into the dimension group called Titles
    • Map the Subtitle column to the Titles dimension group as well
    • Create Actuals as the first measure group:
      • Click the drop down arrow next to Actual
      • Then the right arrow (if expanded will show a drop down arrow as below) for the measure
        Measure group 1.png
      • Double click the name Y Axis (the name of the first measure radio button) and change the name to Actuals, then press ENTER
        Actuals measure group 1.png
      • And select the first radio button, so the Actual column is now mapped into the measure group called Actuals
    • Map Pace into the Actuals measure group
    • Map Target into measure group 2 and call it Target
    • Map Range 1, Range 2 and Range 3 into measure group 3 and call it Ranges
    • Click to Apply the Data Model:
      Apply data model.png
      The data model, structure and test data are now applied in the vizPacker
  10. Prepare the render function:
    • The only lines we really need from the template render function are for creating the vis container
    • So update the render function so it looks like this:
      var render = function(data, container, width, height, colorPalette, properties, dispatch) {
              //prepare canvas with width and height of container
              container.selectAll(‘svg’).remove();
              var vis = container.append(‘svg’).attr(‘width’, width).attr(‘height’, height)
                          .append(‘g’).attr(‘class’, ‘vis’).attr(‘width’, width).attr(‘height’, height);

      };

  11. Add the code from bullets.js:
    • The code is wrapped in an anonymous function that we do not need
    • We do not want that anonymous function, so copy all the code except the first and last line in the file (note they have a red strike through them in these images):
      copy bullets.js code part 1.png
      Copy down to:
      copy bullets.js code part 2.png
    • And paste the code you just copied into the bottom of the vizPcker just before the return render call:
      Paste bullet_js code.png
    • Change the bullet chart from a global namespace (as that is bad practice for extensions – d3.bullet) to a local variable (var d3_bullet):
      • Look for the line that says:
        d3.bullet = function() {
      • And change it to a local variable like this:
        var d3_bullet = function() {
      • We can now refer to d3_bullet when we need to create the bullet chart later on.
  12. Add in the code from index.html to create the bullet chart:
    • Copy all of the script code after the fdata line because we already have the fdata generated in our render function:
      copy index code part 1.png
      Copy down to:
      copy index code part 2.png
    • And paste the code you just copied into the bottom of the render function:
      Paste index_html code.png
    • Change from the d3.bullet call to d3_bullet (the local variable):
      • Look for the line that says:
        var chart = d3.bullet
      • And change the dot to an underscore like this:
        var chart = d3_bullet
  13. Remember I mentioned that the Bullet Chart expects data as JSON, that means we need to convert the data from D3.csv.
    If your D3 extension uses the D3.csv data format there is no need to do this conversion.
    This is how I chose to do this for this example:
    • Copy the code from VizPacker__d3csv_to_JSON.txt
    • Paste the code in just below the vis container and above the bullet chart code
      Paste csv to json code.png
    • Here are the first few lines of that code to convert between D3.csv and JSON formats:
      var fdata = [];
      var meta = data.meta;
      var titleFields = meta.dimensions(‘Titles’);
      var actualFields = meta.measures(‘Actuals’);
      var targetFields = meta.measures(‘Target’);
      var rangeFields = meta.measures(‘Ranges’);
    • Let’s break this code down as it is specific to the vizPacker and how you get the names of the CSV columns that are used in the measure and dimension groups (in vizPacker and later in Lumira as well) that we defined earlier in the DATA MODEL tab:
    • var fdata = [[;
      This is the JSON data variable that was used in the original vizPacker, this is where we will store the converted JSON data.
    • var meta = data.meta;
      Get access to the metadata that is passed in by vizPacker (and later in Lumira as well).
    • var titleFields = meta.dimensions(‘Titles’);
      Get the list of column names from the CSV file that are mapped into the Titles dimension group (this matches to the Titles dimension group we mapped Title and Subtitle to earlier).
    • var actualFields = meta.measures(‘Actuals’);
      Get the list of column names from the CSV file that are mapped to the Actuals measure group.
    • Then we do the same for Target and Ranges measure groups.
  14. That is most of what we need to do because we now have the bullet chart code and rendering code in.
    What we are missing are the CSS styles, we will do that soon.
  15. Change the Bullet Chart so it adjusts to the width variable passed in to the render function:
    • Look for the line:
      var bulletWidth = 960;
    • And change it to:
      var bulletWidth = width;
  16. Add in the CSS styles from index.html to style the bullet chart:
    • Switch to the style tab, go to >> and then select style.css:
      Style tab.png
    • Copy the CSS bullet styles:
      .bullet { font: 10px sans-serif; }
      .bullet .marker { stroke: #000; stroke-width: 2px; }
      .bullet .tick line { stroke: #666; stroke-width: .5px; }
      .bullet .range.s0 { fill: #eee; }
      .bullet .range.s1 { fill: #ddd; }
      .bullet .range.s2 { fill: #ccc; }
      .bullet .measure.s0 { fill: lightsteelblue; }
      .bullet .measure.s1 { fill: steelblue; }
      .bullet .title { font-size: 14px; font-weight: bold; }
      .bullet .subtitle { fill: #999; }
    • Delete all of existing style code from style.css
    • Paste in the CSS bullet chart styles
    • Alter the first line of CSS (the added bit is in bold) to stop Lumira from overriding the font style:

      .bullet { font: 10px sans-serif; !important; }

  17. Validate your code inside the vizPacker:
    • Click on Run Code
      Run code.png
    • If you get a JavaScript error message then there is something wrong with your code:
      • Close the JavaScript error message
      • Right-click on code editor and select Inspect Element to enable the Google Chrome developer area
      • Then go to the Console tab and look at the error messages
      • Fix your code in the vizPacker code editor and then click Run Code again
  18. Once your code is validated, preview your extension inside the vizPacker:
    • Click RUN CODE
    • Click to turn on preview mode:
      Turn on preview.png
    • The preview window will appear inside the code editor and you should see your Bullet Chart:
      Preview in vizPacker.png
  19. Next you are ready to pack(age) and test inside SAP Lumira

Pack and install your extension into SAP Lumira

  1. Click Pack
  2. The extension will be packed into a ZIP file. Click the ZIP file link:
    Packed.png
  3. Google Chrome will download the packed ZIP file
  4. Select the downloaded ZIP file and select Show in folder
  5. Extract out the the packed extension and install it into SAP Lumira:
    • Extract out all the files and folders from the ZIP file
    • Copy the bundles/com folder to <installdir>\SAP Lumira\Desktop\extensions\bundles

      For example: <installdir>\SAP Lumira\Desktop\extensions\bundles\com

      If you already have a com folder you will be asked to merge. That is Okay because like we did for Hello World you should have created a unique ID for this extension to the sub-folders will not clash with another extension.

  6. Next we can test the extension inside SAP Lumira

Test your D3 chart inside SAP Lumira

  1. Start SAP Lumira
  2. Import the bullet chart test CSV file, see the resources section (below)
  3. Select the Bullet Chart extension:
    Select bullet chart.png
  4. Use these settings with the chart:
    Titles Title, Subtitle
    Actuals Actual, Pace
    Target Target
    Ranges Range 1, Range 2, Range 3
  5. The Bullet Chart should now look like this when running in Lumira:
    Bullet chart in Lumira.png

    Note: If this does not work, you will need to refer to the SAP Lumira SDK Getting Started Guide on how to debug within SAP Lumira

  6. That’s it, you have created, packaged and tested your first D3 extension using the vizPacker!
  7. Congratulations and happy coding…

More information

Have a look at the:

Resources

You can find all of the Bullet Chart files on GitHub.

To report this post you need to login first.

15 Comments

You must be Logged on to comment or reply to a post.

  1. Kalyan Verma

    Thanks Matt for the wonderful tutorial. This was really helpful. My CSS/JS/HTML5 are mediocre. The code snippets were very helpful. Having said that, this was a great start for me to get excited and learn some advanced scripting and exploit the SDK as much as possible. 😀

    (0) 
  2. Robert Russell

    Hi Matt,

    Thank you for this blog, I did follow the Hello World blog but didn’t make any progress.

    I needed the examples in this one to progress and the details you provided.

    Cheers,

    Robert

    (0) 
  3. Mike Howles

    Matt,

    Good to see a great blog post from you!  Great stuff.  Hope to bump into you again at a future SAP event.  I’ll definitely be playing with this component (and tweaking it, etc.)

    (0) 
  4. Pablo Nebuloni

    Matt, thanks for all these tutorials about Lumira + vizPacker!

    Do you know if it is possible to feed these graphics (bullet, exoplanets, etc) with another source (eg: SAP BEx) ?

    Cheers,

    Pablo

    (0) 
    1. Matt Lloyd Post author

      Hi Pablo,

      Lumira handles the data access, so if you have BI universes you can look via that route for now, we hope to have more to say about this later in the year.

      Regards

      Matt

      (0) 
    2. Antoine CHABERT

      Hi,

      Just to complete on Matt’s explanation, viz extensions and data acquisition are two separate topics. In other words, any data that is presently accessible to SAP Lumira should be able to feed the viz extensions.

      Best regards

      Antoine

      (0) 
  5. Andrew Jabbitt

    Hey Matt, you rock! Many thanks for taking the time to draft your series on how to extend SAP Lumira. I have been furtling in this space for a little while and was finally able to crack it using your examples 😎 .

    I’ve even created my own blog on how to build a Gauge extension for SAP Lumira:

    SAP Lumira Extension – Taking D3 Gauges to the next level

    Would  appreciate your comments.

    Keep up the good work.

    Regards,

    Andrew

    (0) 
  6. Alejandro Wojtowicz

    Very nice post, it was useful for us!!!

    We were working on another kind of charts and got stucked.

    We’re trying to create a new Lumira component for using Chords charts.

    When we give the lumira component a complete matrix (that means all sources are in the destination field and viceversa) it works fine and the results are as expected, but when trying to draw the chart with a datasource that is not complete (that means not all the sources are in the destintions field also) the chart gives errors or even the visualization is not as expected.

    We need to search into the input data which combinations are not available and if there is any add them to the dataset with a 0 value.

    Does anyone has an idea of how to proceeed to ccomplish this?

    We tried using the MAP function but it returns only the actual data and loose
    the source-destination relationships.

    Thanks in advance

    Regards

    (0) 
  7. Alex Yang

    Hi Matt,

    Thanks for your posting

    I am a fresh man to VizPacker

    I saw in your VizPacker, you only have to tab: JS.File, HTML

    But when I open my VizPacker, I saw render.js/datamapping.js, etc

    Would you kindly let me know how to display the VizPacker same as you (Only two tab in the header for code editor)

    Best regards

    Alex yang

    (0) 
  8. Luis Romero

    Hi, Matt, I’m very greatful for this post, i really need it, but i have a trouble, check it:
    i have the extencion in Vizpacker, but when i put the file of my extencion and star Lumira, the extencion isnt there, i try out whit other archives, for example whit examples extencion and i dont have the same problem, what can i do, any suggestion?

    rgrds

    (0) 
  9. Matt Lloyd Post author

    I updated this for SAP Lumira 1.20 as there were improvements made for creating SAP Lumira  extensions since this was first written (and that is also why vizPacker looked a bit different for some of you).

    (0) 
  10. Prabhjot Singh

    Hi Matt,

    So for any D3 visualization we will need to do the same thing – modify the source code and place the snippets in the respective sections as you described here? Or these steps are just Bullet Chart specific?

    Thanks

    -Prab

    (0) 
    1. Matt Lloyd Post author

      Hi Prab,

      These steps are for the Bullet Chart, although quite a few of the steps will be the same or similar for other D3 charts.

      You may also want to consider doing this with the Web IDE version of VizPacker as that has additional capabilities like debugging and being able to create additional files, you can read more about that here with Dong: SAP Lumira Visualization Extension – Hello World from SAP Web IDE and here: Migrate VizPacker-based Lumira Visualization Extensions to Web IDE

      (0) 

Leave a Reply