Quantcast
Channel: SAP BusinessObjects Design Studio
Viewing all 662 articles
Browse latest View live

New Chart Features on VBX Extensions

$
0
0

VBX Release 1.4 for SAP BusinessObjects Design Studio was released recently.

 

Did created some videos to demonstrate the new features added on the Charts part.

 

1/ Charts now have option to configure different Trendlines

 

 

2/ Drill down charts now have new features to Visualize differently on single click and double click.

Also, now charts can drill down multiple levels

 

3/ Charts now support configuring the Error Bar

 

4/ The user have option to rename the KPI's of Charts


Code to get some information out of an application

$
0
0

Hi Experts,

 

On the project that I`m currently working, we are on the phase of documentation and performance enhancement, and I often find myself needing the list of queries, global variables and the datasources names inside the application, and have not found a way to easily get that list, other than using the designer and clicking one by one, or looking through the BIAPP file to get the desired list.

 

So I developed a little code that will list that information for you, it has helped me so much that I decided to share, maybe it will do the same for you.

 

Attached you will find three files(remove the TXT extension), execute the HTML one "getDetailsOfBIAPP" (Can`t believe I`m going to say this, but open it in IE, for some ungodly reason, when using chrome, things get very slow when you paste the BIAPP file text).

 

Export your application, and open the BIAPP file, copy the text and paste it inside the page.

 

Now select what you want to see, you will have the following options.

 

Capture.PNG

Next page you will get a list of what you selected, if you want any other information, go through the code, it`s quite simple if you look at it, you can fetch whatever you need.

 

I only tested it using BW queries as source, don`t know if anything changes when using other sources.

 

If you know of another easy way of getting this information, please share, I`d love to read your comments.

 

Best

Leandro

Design Studio 1.6 SDK - Component Layouter

$
0
0

Change Log

 

  • 02/04/2015 - Blog posted

 

Planned Enhancements

 

  • More Adaptive Layout Options?
  • Panel-specific enhancements/Grid Support?
  • Flow Layout?

 

Description

Use Case #1

 

There was a recent/timely SCN topic thread (Panel Component and Responsive Design) posted by Jonas Duclos explaining a dilemma that exists today in Design Studio as it relates to Responsive/Adaptive layouts

 

Specifically this part:

 

"My screen have to be split in 2. I want to have a top Panel and a bot Panel. (50%/50% size)

Then i want to have the possibility to hide the top Panel by clicking a button, in order to let the other panel take the full screen (Panel with a graph component)."

 

...

 

"I know the best practice is to use a Grid Layout (in my case with 2 rows with same dimension).

But how can i hide the top row and let the bot row take all the screen if i use a grid layout ? There is no way in Design Studio to change the height of Grid Layout's Rows or Columns by scripting."

 

As perhaps others have come to find, the Design Studio Grid Layout component can be very beneficial at times, but also frustrating at other times.  If you use the Grid Layout component, you are essentially trapped into using a Matrix Layout with a small amount of flexibility in terms of sizing only Rows at Design Time only, and then you really don't have a lot of control over Column Widths without resorting to BIAL sizing math as a workaround, etc etc etc...

 

Use Case #2

 

What about times where you'd like to re-orient, re-position, or hide/show components based on either current browser dimensions, or device type?  Karol Kalisz in the past has provided us the popular Design Studio SDK: Client Information Component that provides you a lot of this information that you can leverage in BIAL scripting and eventing to perform a lot of this logic at the scripting level.  Or, in combination with his also popular Flow Layouter Component  (Responsive Layout with Flow Layouter SDK), achieve near-responsive layout functionality.  In addition, Karol's done some very cool BIAL/ZTL work that I learned a lot from in his document here: Design Studio SDK: Component Manager (for dynamic component access)

 

I think these are all terrific components even present-day, however Karol's approach does rely on a decent amount of BIAL scripting, which some may find a little daunting.  What if we were take take some of the pieces of this and offer design-time spin on it, what could we do?

 

Use Case #3

 

Percentage-based Width/Heights and Margins.  Enough said.

 

Component Layouter is Born

Taking queue from Karol's 'Flow Layouter' and 'Client Information' and 'Component Manager', I present 'Component Layouter'

 

Let's start with a simple DS App layout:

 

adapt1.png

In the component outline under 'Layout', we see a Crosstab (not pictured), 2 Charts, and a Navigation Panel.  Pretty simple layout, and if we stop to consider how we'd take a responsive approach with delivered components to have things resize nicely, you could consider a Grid Layout, with 2 Rows of equal height, and 1 Column, and things would resize pretty decently...  But what about when this happens in that scenario:

 

adapt2.png

Suddenly, that useful navigation panel when real-estate is at a premium is no longer so useful.  We could certainly add a show/hide Navigation Panel button with some script commands somewhere, and that is definitely fine, but what if your browser window was wider than it was taller, and you'd really like those charts to show up side-by-side?  Maybe you don't ever want the drag-and-drop unfriendly Navigation Panel to ever show up on something like a mobile device?

 

So how could we solve this with Component Layouter?

 

First, let me describe what Component Layouter is from an SDK perspective, it is a DIV handler component that is visible at Design Time, but not shown at Runtime.  Even with the introduction of invisible 'Technical Components' with 1.6 SDK, I could not make this one hidden, as the Additional Properties Sheet does not work with Technical Components (aside from one sneaky way of 'tricking' the APS to show up but not a feasible trick to ask of designers) - So for now, it must be a visible component.  See below:

 

adapt3.png

 

Pictured above, we can see the Component Layouter selected in blue, and more importantly the APS on the right.  Below is a larger excerpt, followed by a description:

 

adapt4.png

We have 4 profiles I have created by using the 'New Profile' button.  I have given each profile a descriptive name indicating the scenario.

 

  • 'Default' has a Minimum required Width of 600 and 400, meaning that it would not be applied at browser sizes below that.
  • 'Short' would accommodate heights below 400.
  • 'Skinny' would accommodate widths below 600 but not shorter in height than 400.
  • 'Cramped' would accommodate when width was below 600 AND height being below 400.

 

Note that widths and heights of -1 means 'rules do not apply' and are not checked.

 

Also, I am detecting popular Device types and OS based on browser agent information.  These have also optionally been incorporated into the profiles, if so wished to be used.  Below are the options:

 

adapt5.png

For this example, I'm leaving these set to 'Any' for all 4 profiles, but based on testing in Chrome with device emulation of an Android and iOS devices, this worked nicely.

 

Also of note in the APS screenshot above, you will see something called an 'Item Filter'.  By default, no items are checkmarked and you must decide which components you wish to incorporate into your Component Layouter logic.  Why?  BI Apps become increasingly component-heavy, and I see no point in taking measurements of 60 components when you may only need to mess around with 5 of them, as an example.

 

Speaking of 'messing around' - I am NOT touching the browser DOM, as this has been said is a big no-no by SAP.  Any re-positioning of components that is being done, is all being done using ZTL/BIAL code to maintain browser and application state integrity.  I am also NOT making or hacking together my own container.  Everything is using as standard (as possible) SDK conventions.  That being said, I do perform some unobtrusive Rhino-JS inspections to pull out a list of Components in the canvas, as well as at runtime, inspecting width/height measurements of the browser window and Panel components so that the script layer can make correct determinations on sizes when it comes to percentages.  So speaking of percentages, let's look at the details in the first profile 'Default':

 

adapt6.png

 

As you can see, 3 components are listed, Crosstab is not shown, as we've chosen to filter it out as we are not interested in resizing it.  What you can see here is I am saying I basically want the Navigation Panel to occupy 25% of the width of its container (in this case, the root of the application, but it works inside Panels, also).  I am also then saying I want the Left Margin of the 2 charts to begin at 25% of the window, essentially flush with the Navigation Panel.

 

Next, let's look at 'Short'

 

adapt7.png

 

What this is saying, is that when short is applied, make Chart 1's width 62% and then move Chart 2 next to it and occupy it space to the right, so that they are now next to each other instead of on top of each other like so:

 

adapt8.png

Moving on, let's check out 'Skinny':

 

adapt9.png

This basically is saying let's hide the Navigation panel and stack the Charts on top of one another to occupy 50% space.  Note that Navigation Panel has 'unchanged' listed in its positions (which is the default properties for all fields) - This is useful when you only know you will be messing with visibility and not position/size, and vice-versa.  Just makes things a little more maintainable.  Below is the runtime example of 'Skinny':

 

adapt10.png

And finally, let's check out 'Cramped':

 

adapt11.png

As you can see here, this basically hides everything but the Chart 1 and allows it to take up all available space.  Below is the (obvious) example:

 

adapt12.png

 

In summary, hopefully this serves as a useful example of how you could create an Adaptive UI without a single line of script that hopefully seems intuitive.  But what about more complex adaptive layouts that could not be achieved with these straightforward cases?  There's an event for that

 

adapt13.png

We have 3 items here.  'Monitor Resizes' is what checks the browser for any resizes and then checks against the profiles to determine if it is time to switch.  You can disable this, if you wish.  By disabling, this means it is up to you the designer, to apply profile changes based on your own script logic, with a command such as:

 

adapt14.png

Pretty simple.  Next, I exposed an 'On Profile Change' event.  This fires if you are monitoring resizes.  Why would we need this event if we are managing component sizes in the profiles?  Because it's not always so straightforward.  Imagine a case where it's data selection or filter value-specific or whatever.  Maybe you are not managing ANY components in the Item Filter and thus your profiles have no components.  It is still useful to fire the profile event, and then perform your script logic which may be doing other complex logic.  So scripting definitely has its place here.

 

And finally, I have an 'On Resize' event which I may or may not leave in...  This event fires whenever the browser is resized and you can get a few properties of use:

 

adapt15.png

I feel like Karol's 'Client Information' component already provides this and more, so this is a little redundant anyway, but it's here in this sneak peak for now

 

In closing, here is a video of the simple example at runtime:

 

 

Is this Responsive or is this Adaptive?

You know, I have no idea.  Maybe neither.  Or both.  I'm as confused as you are if you are asking.  Check these links out and decide for yourself!

 

 

 

What you have seen is nearly complete and should be available for download in the usual spot (details here: SCN Design Studio 1.6 SDK Components (ver 3.0)) by tomorrow.  Questions/Comments/Feedback always welcomed.

Your First Extension: Part 7b - Using an Object Array to Create Conditional Formatting

$
0
0

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

 

Last time, we examined the new Array and Object property types, introduced in Design Studio 1.6.  Now we'll put that to use to add some conditional formatting.

 

 

Cleaning up the Extension and App

 

 

Before we go any further, let's remove the example array and object from last time.  They are dead ends, have no future in our gauge component and only serve to clutter up the properties pane, component.js and contribution.ztl.

 

  1. Clear out any values from the Array and Object from last time.
  2. Remove me._arrayExample from component.js
  3. Remove the me.getArrayExample function in component.js
  4. Remove ist functions from contribution.ztl

 

Our strategy for conditional formatting will be as follows:

  • We'll add a new group for managing color, simply to clean of organization in the properties pane.
  • We'll have a default color for the graph.
  • We'll maintain a list of threshold measure value (aka threshold) /color pairs.
  • Whenever we redraw, we need to double check the color.  We do this be running through the list of threshold /color pairs.  If the measure value is greater than the threshold value, then that color becomes the color of the gauge.
  • If none of the threshold values are met, the default color is kept.

 

 

We'll now take these each in turn and implement our conditional formatting.

 

 

We'll add a new group for managing color, simply to clean of organization in the properties pane.

 

To implement the new group, we'll add it to the contribution.xml file

  <group

  id="SCNGaugeColorSettings"

  title="Color"

  tooltip="Gauge Color and Conditional Formatting Settings"/>

 

 

 

We'll have a default color for the graph

 

The colorCode property will remain mostly unchanged.  We'll simply change its display text to "Default Color" and move it to the new Color group.

<property

  id="colorCode"

  title="Default Color"

  type="Color"

  bindable="true"

  group="SCNGaugeColorSettings"/>

 

 

We'll maintain a list of threshold  /color pairs.

 

For each threshold  /color pair, we'll use an Object, with threshold and color as properties.  Threshold will be an integer and color will be of type color; allowing us to use the color picker.  Since we'll have 0..n threshold  /color pairs, we'll wrap that Object into an Array.

<property id="colorArray" type="Array" title="Conditional Formatting" group="SCNGaugeColorSettings">

  <property id="conditionalFormat" type="Object" title="Conditional Format">

    <property id="threshold" type="int" title="Measure Threshold" />

  <property id="colorID" type="Color" title="Color Code" />

  </property>

</property>

 

 

We've got the basics of the properties covered.  We should now have a Color group, with a default color and an Array of Conditional Formatting objects.

 

 

 

Whenever we redraw, we need to double check the color...

 

To do this, we'll introduce a new, component.js only property; called me._displayedColor.  The redraw() function should use this property to color the gauge, rather than color.  Our colorArray also needs a getter/setter function.

//Part 7 conditional formatting

me._colorCode = 'blue';

me._displayedColor = 'blue'

me._colorArray = 1;  //abusing JS duck typing here.  ;-)

 

me.colorArray = function(value) {

  if (value === undefined) {

  return me._colorArray;

  } else {

  me._colorArray = value;

  me.redraw();

  return this;

  }

};

 

var guageArc = vis.append("path")

    .style("fill", me._displayedColor)

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

    .attr("transform", "translate(" + me._offsetLeft + "," + me._offsetDown + ")")

    .attr("d", arcDef);

 

 

Now, before we do anything else in redraw(), we'll go ahead and determine the color of the gauge.  We'll call this new function me.recolor().

  1. Start with colorCode as the candidate color.
  2. Run down the formatting objects in the array.  If the measure value is greater than the threshold value, then the corresponding color code becomes the new candidate color.
  3. The candidate color is applied to me._displayedColor

 

//Recolors the gauge, using the bottommost valid conditional formatting rule

//   and defaulting to me._colorCode if no conditions are met.

me.recolor = function() {

 

 

  // Always default to the color defined in the Color property of the properties pane

  //   If no conditional formatting rules are met, then this will be the color that we use.

  var formattingColor = me._colorCode;

 

  if (me._colorArray != undefined){

  var index;

  for (index = 0; index < me._colorArray.length; index++){

  var conditionalFormattingRule = me._colorArray[index];

  if (conditionalFormattingRule.threshold <= me._measureVal){

  formattingColor = conditionalFormattingRule.colorID;

  }

  }

 

  //Only update me._displayedColor (and trigger a redraw) if the color is actually

  if (formattingColor != me._displayedColor){

  me._displayedColor = formattingColor;

  me.redraw();

  }

  }

  return this;

}

 

 

 

Calling it from me.redraw() only takes a single line of code, at the beginning:

//What color should we use?

me.recolor();

 

 

Now we can test our gauge.  We'll use the following in the test:

  • Default Color is Red
  • Conditional Formatting Rule 1: Threshold = 1000000, Color = Yellow
  • Conditional Formatting Rule 1: Threshold = 2000000, Color = Green

part7b.1.png

 

 

 

When the gauge Measure Value property is less than one million, the Gauge is Red:

part7b.2.png

 

 

 

When the gauge Measure Value property is greater than one million and less than two million, the Gauge is Yellow:

part7b.3.png

 

part7b.4.png

 

When the gauge Measure Value property is greater than two million, the Gauge is Green:

part7b.5.png

 

part7b.6.png

 

As usual, the current state of the project is in Github.

Visual BI Extensions - Trend Icon

$
0
0

I am sure most of you have been in situations where you need an indicator as part of the first visible area of your dashboard showing the status of a set of KPIs.

 

Currently SAP BusinessObjects Design Studio does not provide something "out of the box" for such a scenario and I have seen people using Textboxes and assigning the color dynamically, or using the image component and showing different images dynamically - but so far there is nothing out of the box.

 

That is where one of our new components can help.

 

The Trend Icon allows you to assign a data selection to the component and you can then configure the default behaviour.

 

VBX_TREND_ICON_001.jpg

 

The default behaviour allows you to configure the Trend Icon Label and the position of the label (Right / Left) and you can configure the default icon.

 

VBX_TREND_ICON_002.jpg

 

You can choose from a set of pre-delivered icons and you can then choose the colour and the angle for the icon itself.

 

VBX_TREND_ICON_004.jpg

So after setting up my two Trend Icons, they still look very basic and are so far not acting on any of the incoming data.

 

That is where the conditional formatting of the component helps.

 

VBX_TREND_ICON_005.jpg

 

The Conditional Formatting allows you to setup rules based on a Single Measures, a Measure Calculation, or a Target Value definition. In the example above I created a Target Value definition where I am comparing the assigned value (Revenue for Q1 2016) to the Budget value of the Revenue for Q1 2016.

 

I can then setup several rules - as shown above - and in each rule I can define the Icon itself, the colour for the icon, the size, and the angle.

 

In that way my Trend Icon reacts dynamically based on the actual data that is being returned from the assigned data source.

 

 

You can find out more about advanced capabilities of our Extensions here:

100 Things you can do with Visual BI Extensions and SAP BusinessObjects Design Studio

Design Studio 1.6 SDK - Data Iterator - Read your data row by row finally!

$
0
0

Change Log

 

  • 02/08/2015 - Blog posted

 

Planned Enhancements

 

  • Up for discussion!
  • Iterate via columns?

 

Description

Use Case:

 

Design Studio, being a multi-dimensional client, likes to decompose data into 'tuples' which basically defines each measure as a cell value that is represented by indexed tuples that map back to a dimension member.  There's not really anything "wrong" with this, except that at least in my opinion, this is not the easiest way for me to work with the data in a 2D tabular form.  There have been many SCN threads where people have struggled with basically looping through (aka iterating over) the rows in order to apply some script logic.  Even in my other extensions, the first thing I do is "flatten" the dataset into a 2-dimensional (or table-like) form to work with.  I start thinking about why couldn't I just open this convenience up to BIAL scripting?  This is what I have written this component called 'Data Iterator'.

 

Welcome to the Data Iterator

The Data Iterator is a Technical Component available to add to your Design Studio application.  It is very simple to configure, in that you simply assign it a Data Source, and you are ready to use it in your script.

 

Step 1: Add a Data Iterator Component

di1.png


Step 2: Assign it a Data Source

 

di2.png

Optional Event to note is 'On Data Change' - This essentially fires an event any time the Data Source changes (via filtering, navigation, etc.)

 

That's it.  You're done configuring it.  But what does it do?  It's time to see what we can do with this at script-time!

 

Script-Time Methods

Before explaining the methods, I need to explain conceptually what it is doing with the data.  Let's start with a typical Data Source's result set:

 

di3.png

As mentioned in the use case, the first thing I do is 'flatten' the data internally into an easier to use form.  This means I have some of my own conventions going forward:

 

 

  • getDimensions - Returns a list of dimensions currently in Rows.  This is a simple call that you can do today with DS_1.getDimensions(Axis.ROWS); but there are some subtle differences in some cases.  See example below:

    di4.png
  • getMeasures - Returns a list of items in columns.  Most of the time this is usually Key Figures/Measure selection members, however it will go ahead and flatten in cases where you add a dimension in your Columns as well.  This is where you would begin to struggle using standard BIAL calls to pull this off:

    di5.png
  • getRows(optional offset, optional maxRows) - Returns rows optionally from a given row offset, and optionally a maximum amount of rows.  This is where things can get interesting!  Example:

    di6.png
    So, let's consider the rows here... we have a rows.forEach loop, which returns an element (named row in my example and an index that just comes with forEach, starting at 0...)  The row element itself has some BIAL methods we must explain:

    di7.png

    • getDimensionValueKey(dimensionKey) - Returns the member key for a given dimension key.
    • getDimensionValueText(dimensionKey) - Returns the member text for a given dimension key.

      Example:
      (In this case, key and text was the same.)
      di8.png
    • getMeasureValue(measureKey) - Returns value in float format for the current row's column for the passed measureKey.
    • getMeasureFormattedValue(measureKey) - Returns formatted value in String format for the current row's column for the passed measureKey.

      Example:

      di9.png
      A few things to note above.  On the 3rd line, you see that we can dynamically determine in this case, the measure "key" in the first column position by saying getMeasures().atIndex(index) where 'index' indicates the column you want (beginning at 0).  This is also available for getDimensions().atIndex(index).  This means that you don't HAVE to hard-code a key (like we are doing with 0D_MATERIAL) and have it adapt to whatever the contents are in the result set.  Also note that I switched the output from 'Formatted Text' component to my 'Rapid Prototyping' component.  This is because 'Formatted Text' was stripping out certain HTML markup and I wanted to show some conditional formatting quickly.

 

 

Putting it all together:

After seeing these basic, fundamental methods, what else can you do?  You can go as wild as you want!  If you are accustomed to writing in languages such as JSP, BSP, ASP, etc, the use cases are endless when using Data Iterator along with Rapid Prototyping, as an example.  Below are two proof-of concept examples.  With some additional CSS-clean up and more time, you could come up with some super-easy to create visuals!

 

Simple Table showing only first 10 rows (Could enhance to paginate with buttons etc):

// Get flattened rows from Data Iterator
var rows = DATAITERATOR_1.getRows(0,10);
// Get dimensions (Rows) and Measures (Cols)
var dimensions = DATAITERATOR_1.getDimensions();
var measures = DATAITERATOR_1.getMeasures();
// Start a simple HTML table
var html = "<div style='height:400px;overflow:scroll'><table class='example'><tr>";
// Draw headers for dimensions and measures
dimensions.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
measures.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
html = html + "</tr>";
// Loop through the rows...
rows.forEach(function(row, index) {  // Draw a new row  html = html + "<tr>";  // Write out the dimension texts  dimensions.forEach(function(member, index) {  var dimText = row.getDimensionValueText(member.key);  html = html + "<td class='dimension'>" + dimText + "</td>";  });  // Row striping example  var stripe = "even";  if(index/2 == Math.floor(index/2)) {  stripe = "odd";  }  // Write out the measure formatted values  measures.forEach(function(measure, index) {  var measureVal = row.getMeasureValue(measure.key);  var measureText = row.getMeasureFormattedValue(measure.key);  html = html + "<td class='measure " + stripe +" '>" + measureText + "</td>";  });  html = html + "</tr>";
});
html = html + "</table></div>";
RAPIDPROTOTYPE_1.setHTML(html);

 

Simple Table/Micro Chart:

// Get flattened rows from Data Iterator
var rows = DATAITERATOR_1.getRows(0,250);
// Get dimensions (Rows) and Measures (Cols)
var dimensions = DATAITERATOR_1.getDimensions();
var measures = DATAITERATOR_1.getMeasures();
var firstMeasureKey = measures.atIndex(0).key;
// Figure out Max in BIAL for rendering chart bars
var max = 0.0;
rows.forEach(function(row, index) {  var v = row.getMeasureValue(firstMeasureKey);  if(v>max){ max = v; }
});
// Start a simple HTML table
var html = "<div style='height:400px;overflow:scroll'><table class='chart'><tr>";
// Draw headers for dimensions and measures
dimensions.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
// Draw first measure header
html = html + "<th>" + measures.atIndex(0).text+"</th>";
var w = 300;
html = html + "</tr>";
// Loop through the rows...
rows.forEach(function(row, index) {  html = html + "<tr>";  // Draw a new row  dimensions.forEach(function(member, index) {  var dimText = row.getDimensionValueText(member.key);  html = html + "<td class='dimension'>" + dimText + "</td>";  });  var measureVal = row.getMeasureValue(firstMeasureKey);  var measureFVal = row.getMeasureFormattedValue(firstMeasureKey);  var barWidth = w * (measureVal / max);  html = html + "<td style = 'width:" + (w+200) +";'>";  html = html + "<div style = 'display:inline-block;width:" + barWidth + "px;background-color:#006699;'> </div>" + measureFVal;  html = html + "</td></tr>";
});
html = html + "</table></div>";
RAPIDPROTOTYPE_2.setHTML(html);

 

Runtime Example of both:

 

di10.png

Super Goofy Scorecard Example:

 

// Get flattened rows from Data Iterator
var rows = DATAITERATOR_1.getRows(0,10);
// Get dimensions (Rows) and Measures (Cols)
var dimensions = DATAITERATOR_1.getDimensions();
var measures = DATAITERATOR_1.getMeasures();
// Start a simple HTML table
var html = "<table class='example scorecard'><tr>";
// Draw headers for dimensions and measures
dimensions.forEach(function(element, index) {  if(element.text != "Key Figures"){  html = html + "<th>" + element.text + "</th>";  }
});
measures.forEach(function(element, index) {  html = html + "<th>" + element.text + "</th>";
});
html = html + "</tr>";
// Loop through the rows...
rows.forEach(function(row, index) {  // Draw a new row  html = html + "<tr>";  var priorValue = DATAITERATOR_1.makeNull();  // Write out the dimension texts  dimensions.forEach(function(member, index) {  var dimText = row.getDimensionValueText(member.key);  if(member.text!="Key Figures"){  html = html + "<td class='dimension'>" + dimText + "</td>";  }  });  // Row striping example  var stripe = "even";  if(index/2 == Math.floor(index/2)) {  stripe = "odd";  }  // Write out the measure formatted values  measures.forEach(function(measure, index) {  var trend = "";  var measureVal = row.getMeasureValue(measure.key);  var measureText = row.getMeasureFormattedValue(measure.key);  if(index>0 && !DATAITERATOR_1.isNull(priorValue) && !DATAITERATOR_1.isNull(measureVal)){  var delta = Math.round(measureVal / priorValue * 100) + "%";  var icon = "";  if(priorValue > measureVal){ // Down  trend = "downward";  }else{ // Up  trend = "upward";  }  measureText = "<div class='icon'></div><br />(" + delta + ")</span><br />" + measureText;  }  if(!DATAITERATOR_1.isNull(measureVal)){  priorValue = measureVal;  }else{  priorValue = DATAITERATOR_1.makeNull();  measureText = " - ";  }  html = html + "<td class='measure " + stripe + " " + trend + "'>" + measureText + "</td>";  });  html = html + "</tr>";
});
html = html + "</table>";
RAPIDPROTOTYPE_3.setHTML(html);

 

di11.png

 

Oh, and PS here's the CSS for my examples:

.example {  border-collapse : collapse;
}
.example .dimension {  background-color : #006699;  color : #FFFFFF;
}
.example th {  background-color : #006699;  color : #FFFFFF;  font-weight : bold;
}
.example.scorecard th {  /*white-space: nowrap;*/  padding : 20px;  background-color : #0099CC;  color : #FFFFFF;  font-weight : bold;  font-size : 20pt;
}
.example .measure {  text-align : center;
}
.example .icon {  display : inline-block;  width : 48px;  height : 48px;
}
.example .downward .icon{  background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH2QcVCAsthDpI/AAABQ9JREFUaIHtV0tsG0UY/ma9XnudJq1Tp2ppRRMeF6jopVKFEIWq9NACogipQE9wQEgc4AYmAqkXWpUb0CvigopED1CqJgdSFBCoIFBJk5YWEhKnjR/JOonf+5wZDrEbs971ay0liHzSL8+u5//n++af+WcW2MAGNrCBDfyfQbw49x89zTtBQjDIlumRaLYdX9Hr4CdeeBIoy+AAOOcrjxxgnCOTLa6+A8D5Sp9K+/ufxnQv43sWAA6M/Z12/otzKIs5V9eH+rd6Hl7wGoDx9lcR9+BbgWcBXjh4EV/BGmfA6+gdEOAF62IJMdY+CQ+ud7G2e6ADCtZ0D3QCHciAlyW0LjLgwXc9LCEvWB9VyMs54HVwrPUe6MAScr3M9R89tR8gPwMACEqOnbi3Mlq+mQYgEaX/6dOmQ/wQAAggj0wPRSecYtT9Hhh45tRrASlw9sBjeyVBEEAZW70Ws5Vfvyji9kLehWD92+i2cAiU0ruTwCsZIYBlUsRisxQMJ2aGol+6xfDVE5D56/LV7vsOkXxe3b9je8SfzurIFk0UVAtFbcVyJaNuFkqq+3Vf1S1oBr1ruslAGYcoEMzdiReYyd6eGY5+Vo9jXQEAkJkc+SGw6/E9Jd144N6dEX9JM0EZL6e/8RKqJ4DbjAgEoaCIZDxZ0E3zXOxS9P1G/JraxHJIfSWRUMZmYwlja48MQjx9iToTIQRdQT8WkoquqfqVWHDgjab8mul04/xJg2j8yOTMXFxRFlm4O+CNrQ2EAHJARHYpYxaKxSldUI/h/HHajG/TZXR6JJoVKTlw42ZsuVgs8c1dUvuMbQhKIkrFAltaWlJESg8mLp50rnoOaOkcmBp+Z44wdnB84s8Csyx0Bb1/Ugf8PliGgfS8UoLADk0NDyqt+Ld8kE0PRScsgx67Nnar6PetEGgXkiiAMIpUMqEyxp+LXRy81WqMtkbPTl2e6bn/YGpxKXd4184+P+Xup6pbFfIJBKIgIBlPqJzS12PDgxfa4dL29C1PXv5908ATvYWCunfH9ohkUuZYUp0ECIQg4BegJJMFy6JnZobe/aRdHu3nH0BmcuTb0O7v9pkmHeiLhEXTYjV97AIIAQKSiMWFhaKh69/MXIq+5YWDx8sc4bKsvTQ/nx5PJhaMUEBEoyNCEn3IpNO6pqoTsqy96m381jNAyj4VE5U/Rokk9X1VIvKLXaFQd1dIFiy6monqDEiigFI+T3PZbLw0O/5UbPSsXo4jlK3lE7IZAZXgYtl8VW0RgL+4cJ0GN22/mDcDL/ds6ZYDkp/Q8qauCBB9Akxd40uLi8ta4vrh+fFzy1XkfbZ2RQxBg8+GegKqA4u2do2QQmpCDfbcM5ozfMfDvWG/KPrAGEdJ1eETCLhlQZlPqVrq5vOpq5/P2khXW4V4dRtuQtwE2INWi3GyFRHJa8ty5MHJvIbDka29fggEqmqAcIp0KqHpmdtvJn/59FfUzrKTOaFGhJOAivp65jRzAgBf/s5vsdC2PYWiRh/t7QuLqqohnUpqRkb5MP7jx1/XIedIsBHcMlBvZhoSyM1euSbv3BfWVOvhfC5n6MXsF3OjZz4CwMrGq9rM9r6R1RB1g9PadHu2r1sAsjBw5L0LDFyYHf7gWUCtlKYKeY5/C7GLchJZg2bKlhNZt81WnSESiuyWeGCzT42Pq1idPfuM2sU4iXJFq3XXjXANeRd/NxFOgpom1Ak0Q74Cu4gN/KfxDxmvmYQNdjkRAAAAAElFTkSuQmCC)
}
.example .upward .icon{  background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAG/klEQVRoge2YbYxU1RnHf8+5d3bZl8qLoryJKC2sChHEZdkXikBQKVIxqQnQNumX1gZDU+OKxk/W+KH9oI39aG3TmhoTP2FCSkJTG0SWLW2atMpGKtiFEXZZXnf2ZWZn7j1PP9x7d+7MzsosM7Rpus/m5Jw5M/c8//95XvfCtEzLtPxPi9ysg9s6nW2oHHDVW/zh6yRvlh5zMw5tfbZmpesk3n2gaa21jnN4zQvMvBl6AJxqH7h+L3OpoXv7hl2zH2raYNLeUMPlgb6NLQvtb3t60Grrq6oFtu6l1q91P9jw0DfmzL11vhw+9S7rVmyuuevOZavOLXHeqqauSKppAbljg/veyuXNLa33b6rp7n2fkewg1zIXWH/f9sTZ86eWzXlw1E922aNV1Fk9Au2d7isL71j63e3tu+r+mjzIaHYQVctI9hrp3Ahr792S+OzMJ63z1uZOJLv0ZLX0VoVA6/POrlmNt/1s92N76k9ePMaV0fNYLIrFqmUwPUCNW8sDS9tqPu39+JuLmuVQstuer4buimOgozPRUpuofXP31j1151MnGRg+C4AU/X16oZsMKbZ27KjD1YMt+1hUMXoqJNCyj0UYDu7c8sPGjB3izNUeBGFCeREQEf6WPER9Y4O0rd40x1XncMtebqlEP1TgQg/voZGE8+dt63fNmz93sZzoO4KvPqqB24Bi1UdRrFpULb569KdOsfqejWKtV38xNdC6boF9p5L0ekMWeOopnGyju79t1ZaFK+5eaz7pOxKCDkREokXBc4Iw5qU5cvo9Hryvo/bOeUvazi1x3rxR8HCDFpi9zXlj+V2rntjW+u0Zfz/3RzLeSNHN2/HP8TkaGW+UK6Pn+fqKJxNf9Pcuu3V1Zih5zB7/jxBo70w8ffvsBS9957Ef1/3z4nFSmUuFQJkIvJiADdPrWG6Y1nsfrTl1pufh+c3ex8ljU0+vUyLQ1uluapjxld98f8dL9ReGP2dg+MwEoIHflyZgYzGiKIOZi9QkZrDqax2Jz872bF/QbA580e0PTAVT2THQ+mztVx3H7P/e4881jHqD9KVOA4qi47MIiAFEERMfwb5I9Mt8zPb0HyVtr7Kl/fEGce2fpppeyyLQ8SKzjesd3vnInsaG+gb+dfkfqCqqGiRNA8YBcYLZcQQxgnEMxpHxPeOAGh8xtiC+/3L299Q11Ev76k0zHdxDa16mvlwC13Wh+1+mpm7M/eCRlm8tXbl0ndPT34WvORQfxKImmm24p6goiB0fViw5m8bTTGgdCawlAgKqSn/qc9bcs9n4vjfzyoWB9nLT63UJLGt2fr26qWPjo807a0/0HyHnp1HsOGjEglFULIiC+LE9nzE7QtpPYcmNF7RgUEDEtzn6Uqdpa9rmXL42MO+cO3h7sksPVkSgrdN94e55y5/ZveVHdScHjjOauxYCtTHAFhEFk99TsWTtCEPZi+Q0AyYEa8AYmWCBaJ3101wZ6WPDyicTyb7eleWk10n/pWzrdJ6Y1XjbO8/v/nmD6yTw7FiQXcKgNcagWA73/g6LF44cvua4mukj7Q0hkPcBJYwbUAtqlR1N+4LP4X7Bb3x4+8Abo+n0yI6jr3l/mAynOyk1lf2pkWv85Fc/KGRsyKhasrnsjFef+WUQkCEpz89wKZPEag7Hjd2NEgINhygautEv3n4VMWQm6gdV6oFDX3bRkxLoes0T8ICxAvwdLzIrfPQKKJgAfM4f5lK2FzWKiVyD0AIhcMLbtya44fA3vjjeglIYBPDHSpArh8Akoh/9lKsAbc+FGkTJ2hEuZ88iRgMfL25Io5u3oAbwA2JhXOSiM29EpkqgkA0WKzkuZXpBLMYJM4wp7OPi4KOeL0i1lWgPpCICiHIpcwY1XljM4hmGsPQqaOg2FiyKQbBU56VURQTSXgqPsfH0aIwgjmBMPkWigqqCzWckiyIqFLfb/wUCg/nCNJ7nCy0RuU/QZoNoZCGtBv7KCFhsrBARIxL0PWIEtYpFgpqnIFbzLlYFqe6rxZKgbtrrV2DqFjAE7Ue+BQliNF+krKJCcOtW83vROjZiUg9YwA+HpUwph0AE2I2tHeKgibUISBCkUZqJ2oOwfYjacM0XNwEaY+Cj4cXWN0TAhN9HwIvX5PwMLgnG7Fh464E5RCmqxAEBayMiAZmYGeqKQHuhnmjtMYlVJiMgFN62W2JQ59zC5sVPx9pkxoM6f0wAMt+sBRZBwffHCdSEIEu8VAobrfwoi0BZ8vpbr1TyeCS1lTz8ZSniei5UKi6c8DlD4W1Gt2cpDNZif4/PxeuSLlROjpssiIsBR2spGnECcSJRtilFqOwgnmqSjqfRYtDxudTZ+U4iT6KYzJTTaDWqTHRG/PYnK5AR6Ag4lAjMaZmWafk/kn8DlgvUqMpTMAIAAAAASUVORK5CYII=)
}
.example .downward {  color : #FF0000;
}
.example .upward {  color : #009966;
}
.example .measure.even {  background-color : #FFFFFF;
}
.example .measure.odd {  background-color : #DFDFDF;
}

 

 

 

What you have seen is available for download in the usual spot (details here: SCN Design Studio 1.6 SDK Components (ver 3.0)). 



Questions/Comments/Feedback always welcomed!

Design Studio SDK: Going into M mode with more components

$
0
0

It seems the scn sdk release 3.0 is now stable - at least not much complains arrived at Issues · org-scn-design-studio-community/sdkpackage · GitHub.

 

It means slowly, we can add new stuff. Mike has already started with new components. I have taken some time and "migrated" today components into M mode I have missed most (and not only). The reason is, I try now to use the M mode for building new reporting apps, and some of the components were missing...

 

The components are:

 

* Action Sheet Button

actio.PNG

* Date Range Slider

scr.PNG

* Date range Scroller

scro.PNG

 

* Text Area, also new property for "rows" is available now.

tex.PNG

 

* Activity Viewer - d3 based components, we missed it on first migration run.

act.PNG

 

the M library SAPUI5 Explored offers more controls which we can make available - if you feel you need one, place an issue with the request. As soon we have capacity, someone will add it..

 

Help / API Specification

Go directly to the help site and find the component: http://org-scn-design-studio-community.github.io/sdkinstall/web/components/index.html

 

Download & Use

This component is available on the community package, as in SCN Design Studio SDK Development Community

 

Example as ZIP:

Release Client Information & Flow Layouter · org-scn-design-studio-community/applications · GitHub

 

Any thoughts?

feel free to add as usual...

 

Have fun.

Design Studio SDK: Link Component in M library

$
0
0

In the journey to M mode, I was missing a simple Link component, so now it is introduced into the palette: Fiori Link.

 

What you get?

It is a simple Link, but it has one special behavior - which is coming directly from SAPUI5 implementation. If you pass the HREF directly, it will call the URL first and then the event onPressed() will be executed.

 

Visualization

Nothing special on this, just a simple link.

lin.PNG

Properties

fl2.PNG

Help / API Specification

Go directly to the help site and find the component: http://org-scn-design-studio-community.github.io/sdkinstall/web/components/index.html

 

Download & Use

This component is available on the community package, as in SCN Design Studio SDK Development Community

 

Any thoughts?

feel free to add as usual...

 

Have fun.


Design Studio SDK: Fiori Busy Dialog Component in M library

$
0
0

In the journey to M mode, I was missing a simple Link component, so now it is introduced into the palette: Fiori Busy Dialog.

Demo in UI5: SAPUI5 Explored.

 

What you get?

With this dialog, you can cover the screen allowing "Close" button, which can be used for event.

 

Visualization

Busy + Close

bus.PNG

also, you can use this dialog to show some user facing messages..

bu1.PNG

 

Properties

bu2.PNG

bu3.PNG

bu4.PNG

Help / API Specification

Go directly to the help site and find the component: http://org-scn-design-studio-community.github.io/sdkinstall/web/components/index.html

 

Download & Use

This component is available on the community package, as in SCN Design Studio SDK Development Community

 

Example as ZIP:

Release Client Information & Flow Layouter · org-scn-design-studio-community/applications · GitHub

 

Any thoughts?

feel free to add as usual...

 

Have fun.

Your First Extension: Part 8a - (D3) Adding Guide Lines

$
0
0

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

 

Up to now, our gauge has been a colored arc and that's it.  Our emphasis has been on Design Studio specific concepts.  With this set of installments, we're going to start cleaning up and refining the component for professional use.  We'll be adding guide lines, a pointer, animations and text decorations.

 

Part 8 will be about the guide lines.  As in the early parts of this series, we're going to step out of Design Studio and investigate drawing the guide lines with raw HTML and D3, before we add them into our component.

Part8a.1.png

 

What you see above is the goal of this installment.  We're going to draw three guide lines.  The first is a circular line at the outer radius; having its own start and end angles.  This could be used to outline the gauge, even when no arc is present (e.g. if it were empty or we were using only a pointer.  The other two start at the origin and follow the start angle and end angle-max.

 

 

Drawing the Ring Guide Line

 

What we're going to do here is draw a second arc, starting at outerRad and with a given thickness.  It will have its own start and end angles.  Effectively, it's a rehash of what we did waaay back, in Part 2a.  ( http://scn.sap.com/community/businessobjects-design-studio/blog/2015/10/06/part-2a--your-first-steps-with-d3 )

///////////////////////////////////////////

//Lets build a border ring around the gauge

///////////////////////////////////////////

var visRing = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

var ringThickness = 2;

var ringOuterRad = outerRad + ringThickness;  //Outer ring starts at the outer radius of the inner arc

var ringColorCode = "black";

var ringStartAngleDeg = 0;

var ringEndAngleDeg = 360;

 

 

//Don't let the arc have a negative length

if (ringEndAngleDeg < ringStartAngleDeg){

ringEndAngleDeg = ringStartAngleDeg;
alert("End angle of outer ring may not be less than start angle!");

}

var ringArcDefinition = d3.svg.arc()

.innerRadius(outerRad)
.outerRadius(ringOuterRad)
.startAngle(ringStartAngleDeg * (pi/180)) //converting from degs to radians
.endAngle(ringEndAngleDeg * (pi/180)) //converting from degs to radians

 

 

var ringArc = vis

.append("path")
.attr("d", ringArcDefinition)
.attr("fill", ringColorCode)
.attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");

 

 

Drawing the Start and End Guide Lines

 

 

Again, we are rehashing what we already covered in Part4b ( http://scn.sap.com/community/businessobjects-design-studio/blog/2015/12/08/your-first-extension-part-4b--the-positioning-visualizer ).  There, we used a line accessor function to visualize the padding boxes by drawing them.  We'll do the same here.  What's different is that the start and end points will need to be calculated, so rather than the lineData being a simple array of x/y value pairs, it will be built dynamically.  It will use the line length (i.e. the radius of the gauge; outerRad) and the angle (either start or end) and use a bit of trigonometry to get the x and y points.

 

The line data is defined by the counterpoint and the points on the outer radius, where radial lines from the angle would intersect.  We'll use a helper function, endPoints(), to to the trigonometric calculation.  The looking at the function, you see that the brush stroke of the radial line on start angle actually starts at the ring and goes to the centre and then goes back out to the ring at end angle.

var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];

//Helper function

function endPoints (lineLength, lineAngle){

  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));

  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));

  return {x:endX, y:endY}

}

 

Using these two to help draw the radial lines:

///////////////////////////////////////////

//Lets build a the start and end lines

///////////////////////////////////////////

var bracketThickness = 2;

var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];

var visStartBracket = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

var lineFunction = d3.svg.line()

  .x(function(d) { return d.x; })

  .y(function(d) { return d.y; })

  .interpolate("linear");

 

var borderLines = vis

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

  .append("path")

  .attr("d", lineFunction(lineData))

  .attr("stroke", ringColorCode)

  .attr("stroke-width", bracketThickness)

  .attr("fill", "none");

 

 

//Helper function

function endPoints (lineLength, lineAngle){

  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));

  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));

  return {x:endX, y:endY}

 

 

 

Putting it all together in a test webpage

 

That's it.  Next time, we'll adapt these features to the gauge component as it now stands.

 

<!DOCTYPE html>

<html>

  <head>

  <meta http-equiv='X-UA-Compatible' content='IE=edge' />

  <title>Part 4</title>

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

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

  <script>

  var vis = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

 

  var pi = Math.PI;

 

  //Viz definitiions

  var innerRad = 0;

  //var outerRad = 70;

  var width = 200;

  var height = 200;

  var startAngleDeg = -45;

  var endAngleDeg = 45;

  var colorCode = "red";

 

 

  //Outer Dimensions & Positioning

  var paddingTop = 10;

  var paddingBottom = 10;

  var paddingLeft = 10;

  var paddingRight = 10;

 

  //The total size of the component is calculated from its parts

 

  // Find the larger left/right padding

  var lrPadding = paddingLeft + paddingRight;

  var tbPadding = paddingTop + paddingBottom;

  var maxPadding = lrPadding;

  if (maxPadding < tbPadding){

  maxPadding = tbPadding

  }

 

  var outerRad = (width - 2*(maxPadding))/2;

  //var width = (outerRad * 2) + paddingLeft + paddingRight;

  //var height = (outerRad * 2) + paddingTop + paddingBottom;

 

  //The offset will determine where the center of the arc shall be

  var offsetLeft = outerRad + paddingLeft;

  var offsetDown = outerRad + paddingTop;

 

  //Don't let the arc have a negative length

  if (endAngleDeg < startAngleDeg){

  endAngleDeg = startAngleDeg;

  alert("End angle may not be less than start angle!");

  }

 

  var arcDef = d3.svg.arc()

  .innerRadius(innerRad)

  .outerRadius(outerRad)

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

  .endAngle(endAngleDeg * (pi/180)); //converting from degs to radians

 

  var guageArc = vis.append("path")

     .style("fill", colorCode)

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

     .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")")

     .attr("d", arcDef);

 

  ///////////////////////////////////////////

  //Lets build a border ring around the gauge

  ///////////////////////////////////////////

  var visRing = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

  var ringThickness = 2;

  var ringOuterRad = outerRad + ringThickness;  //Outer ring starts at the outer radius of the inner arc

  var ringColorCode = "black";

  var ringStartAngleDeg = 0;

  var ringEndAngleDeg = 360;

 

  //Don't let the arc have a negative length

  if (ringEndAngleDeg < ringStartAngleDeg){

  ringEndAngleDeg = ringStartAngleDeg;

  alert("End angle of outer ring may not be less than start angle!");

  }

  var ringArcDefinition = d3.svg.arc()

  .innerRadius(outerRad)

  .outerRadius(ringOuterRad)

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

  .endAngle(ringEndAngleDeg * (pi/180)) //converting from degs to radians

 

  var ringArc = vis

  .append("path")

  .attr("d", ringArcDefinition)

  .attr("fill", ringColorCode)

  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");

 

 

 

  ///////////////////////////////////////////

  //Lets build a the start and end lines

  ///////////////////////////////////////////

  var bracketThickness = 2;

  var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];

  var visStartBracket = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

  var lineFunction = d3.svg.line()

  .x(function(d) { return d.x; })

  .y(function(d) { return d.y; })

  .interpolate("linear");

 

  var borderLines = vis

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

  .append("path")

  .attr("d", lineFunction(lineData))

  .attr("stroke", ringColorCode)

  .attr("stroke-width", bracketThickness)

  .attr("fill", "none");

 

  //Helper function

  function endPoints (lineLength, lineAngle){

  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));

  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));

  return {x:endX, y:endY}

  }

  </script>

    </head>

  <body class='sapUiBody'>

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

  </body>

</html>

Design Studio 1.6 - Visualizing Row Selections in Scorecard

$
0
0

Starting u with the scorecard component (Design Studio 1.6 - View on Scorecard Component) and understanding the property selection models (Design Studio 1.6 - Advanced Selection in Scorecard) brings us to next topic - customization of the visualization.

 

Introduction

Basically, the scorecard component primary use is nice visualization of data with flexibility in column definition. For this reason, some "table" properties (as scorecard "runtime" is sap UI5 table) have been overwritten in the component specific CSS. Otherwise many properties on cell level would be not possible to overwrite (borders, backgrounds).

 

Now, there is also selection option in scorecard, actually 4:

 

* cell

* single row

* multiple row

* multiple row (toggle) - this is like multiple, but you can toggle the rows.

 

When you switch on the selection, you get a scorecard like this one:

sc1.PNG

 

On left side, you can see additional column which allows you to select the complete row. This is, in comparison to the cell selection (which is handled actually as cell click and does not "select" anything) a way for selection of the row - and by that it is reflected also in the scorecard model.

 

The Question

Is it possible to visualize the selected row?

 

In standard, scorecard is only visualizing selection in the first column, which is specially there for the marking of selected row. But, as there is information of the selected row, and all cells know that they are in "selected" mode, you can change the behavior.

 

How to Visualize Selection?

 

First,

you need to pay attention that the cells have possibility to define background. default is white, so you need change this definition. -> put all cell backgrounds on "transparent". This is what you get:

sc2.PNG

ok, not exactly nice - but important for the first step. Explanation is technical, so only for those who want... the background of the cell is in DIV inside of TR>TD, and the row is actually TR element.

 

Second,

let us make the scorecard again nice - I put on scorecard additional css class:

 

.myCustomScorecard .sapXTableMain {  background-color: white!important;
}

the class "sapXTableMain" is assigned to scorecard component, so when you define overwrite rule, you can change some properties.

 

do not forget to assign new class to the component itself.

sc4.PNG

 

what this will change? now the background is set on scorecard component, and the cells are transparent. result:

sc3.PNG

 

ok, now our scorecard is looking same as in the beginning, but is prepared for more changes.

 

Third,

now, I give you 2 mode classes, which you can overwrite to bring:

 

1/ the hover effect

2/ the row selection effect

 

HOVER

.myCustomScorecard .sapXTableMain tr.sapUiTableRowHvr>td {  background-color: red!important;
}
.myCustomScorecard .sapXTableMain tr.sapUiTableRowHvr {  background-color: red!important;
}

SELECTION

.myCustomScorecard .sapXTableMain .sapUiTableCtrl tr:not(.sapUiTableGroupHeader).sapUiTableRowSel > td {  background-color: green!important;
}

Making this changes, you get following result:

sc6.jpg

 

and animated..

20160211-175136_capture.gif

 

Now, the hover effect is RED and row selection is GREEN. So you can better mark the selected row(s) and visualize it for the users.

 

Example

The example can be downloaded here:

applications/SCORECARD_SELECTION_MODEL-20160211175303.zip at master · org-scn-design-studio-community/applications · Git…

 

hope it helps

Design Studio 1.6 - Layout for Simple Printable Report (PDF)

$
0
0

My Requirement

I came across the requirement to have a simple, "live" report which brings data from BW and CSV data sources and is extendable in simple manner. In addition, I want to print it out and take with me as PDF.

 

The Idea

This pointed me out to following (technical) requirements.

 

1. I want to have a reduced complexity in terms of tabs, screens etc

2. it must be fitting screen on big and smaller devices

3. it should be modular for better extensibility

4. with one click it must be printable

 

I have taken a look on the components which are available in the scn community package and I have used the M mode for the design.

 

The Implementation

As layout I have taken standard panels which are positioned by flow layouter (as in Responsive Layout with Flow Layouter SDK) (it can be also made by any other layouter component, eg. Mike's Design Studio 1.6 SDK - Component Layouter). With this, the layout is re-arranging dependent on the screen size.

 

20160211-203942_capture.gif

 

The layout in general is quite flat, header (Fiori App Header component), small buttons on left and right side (Action Sheet Button component), and then the flowing layout with panels in the middle.

 

How Does It Work?


PDF,

keep in mind one goal was to have easy PDF export - this is why the size of the panels is as it is. On Left side, you can find a button with action "export to PDF", then, using A4 landscape, the panels are fitting exactly on each page one. So, no effort of getting the data out.

* see function printStatusReport (there are panels listed for print).

 

Excel,

Another function is to get the data into Excel, so there is a invisible panel, including a crosstab (positioned in the panel, but if you want to make it invisible, do not turn off the visibility, position it with LEFT=-1000 or so. With this, you can export and close the dialog.

 

Reordering

Only what you have to do, is to add new created panel into the array, method SCRIPT_STARTUP->fillCollectionsPanels().

 

Visibility

There is another method in the global scripts which is handling visibility of the panels. The method is just looping on the registered panels, adding it as items into the action sheet button (dynamically) and also assigning icon depending on visible state.

 

KPI Tiles

Those are getting data on startup, but you can also make it bindable directly to the data source, up to you.

 

Filters

On the right side, there are filters - the most interesting one is the "generic filter" which shows Filter Panel, based on generic ACTION KEY in the component. The corresponding method is in the global scripts

 

Extensibility?

On this infrastructure, if you want to extend the report, you need only to create new panel, register in 2 places and add new data source. With the automatic visibility switch, you do not need to extend any buttons any more. The layout is flow layout, so you get this panel at the bottom and the report will be simply longer.

 

Detail Information

Whenever you need some detailed information, you can always add some "dialog" with this part, similar to the export dialog and jump from the main report.

 

The working App (based on dummy CSV data set)

applications/SCN_MY_REPORT-20160211205251.zip at master · org-scn-design-studio-community/applications · GitHub

 

I hope some of you will find it inspiring.

 

Closing Words

This app was build with SCN SDK components, you need to install in order to run it. See SCN Design Studio SDK Development Community.

BUT, the idea is (implementable to some extend) w/o SCN extensions.

Design Studio SDK: Improved Checkbox Group

$
0
0

In alignment on the old blog Design Studio SDK: CheckBox Group (with Image) Component I had an additional requirement to make a horizontal check box group.

 

So I have extended this component and now it has 2 more properties.

cb2.PNG

 

With this properties, you can change the layout to "Horizontal" and then the check box group is looking like that:

cb1.PNG

 

very useful for small number of selections (like years).

 

The other events and scripts are usable as before.

 

In addition, the component works now also in M mode!

 

Help / API Specification

Go directly to the help site and find the component: http://org-scn-design-studio-community.github.io/sdkinstall/web/components/index.html

 

Download & Use

This component is available on the community package, as in SCN Design Studio SDK Development Community

 

Any thoughts?

feel free to add as usual...

 

Have fun.

Your First Extension: Part 8b - Adding Guide Lines to a Component

$
0
0

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

 

Last time, we added  guide lines to our raw html sandbox gauge.  Now we're going to bring it into our component.  We have a few things that we'll need to take care of:

 

  • Since we won't always want to display the radial guide lines and guide arc, so we'll need to make them optional.
  • In part 9, we'll introduce an indicator needle.  When we do that, the colored arc won't be our only display option, so while we're at it, we'll add the main arc to the optional list.
  • The guide ring has the same clockwise orientation as the main gauge arc and if the end angle is smaller than the start angle, it will need to be increased in 360 degree increments until we get a clockwise arc.  You may recall from Part 6a how we solved this.  We won't simply re-use the recalculateCurrentAngle() that we created there, as most of ist code is about measure handling.  Instead, we introduce a new function, recalculateGuideRingAngles, that simply does the while (end <  start) {//Increment;} part. 
  • The guide lines and arc need a color setting, as we may not want them to be the same color as the main gauge arc.
  • The guide ring (arc) will need its own start and end angles. 
  • The guide ring and lines will need their own thickness setting. 
  • For the sake of simplicity, we'll presume that these new settings are the sort of thing that designers want to have precise control over at design time and don't need to be dynamic.  Therefore, the new properties won't be added to the Design Studio script language and we won't need to change contribution.ztl.
  • We'll add a new "Guide Lines" group in the properties pane, where we'll cluster the new properties.

 

 

 

Adding the Properties

 

We'll add the new group to contribution.xml

  <group

  id="SCNGaugeLineSettings"

  title="Guide Lines"

  tooltip="Guide Line Ring and Line Settings"/>

 

 

We'll add the 8 properties, that we defined above as requirements:

<property id="enableGuideLines" title="Enable Guide Lines" type="boolean" group="SCNGaugeLineSettings"/>

<property id="guideColorCode" title="Guide Line Color" type="Color" group="SCNGaugeLineSettings"/>

<property id="bracketThickness" title="Guide Line Thickness" type="int" group="SCNGaugeLineSettings"/>

<property id="enableGuideRing" title="Enable Guide Ring" type="boolean" group="SCNGaugeLineSettings"/>

<property id="ringColorCode" title="Guide Ring Color" type="Color" group="SCNGaugeLineSettings"/>

<property id="ringThickness" title="Guide Ring Thickness" type="int" group="SCNGaugeLineSettings"/>

<property id="ringStartAngleDeg" title="Guide Ring Start Angle" type="float" group="SCNGaugeLineSettings"/>

<property id="ringEndAngleDeg" title="Guide Ring End Angle" type="float" group="SCNGaugeLineSettings"/>

 

 

If this component were for production use, we'd be very defensive, checking for the presence of intiialized property values whenever we need them, etc.  For the sake of brevity, we'll give the new properties default values.  This is less reliable than actively checking for undefined property values, but also a lot less verbose.  We'll default to guide ring/lines being blue, not being displayed, being 2 pixels wide and being a full circle (from 0 to 360 degrees).

<initialization>

        ...       

        <defaultValue property="enableGuideLines">false</defaultValue>

        <defaultValue property="guideColorCode">blue</defaultValue>

        <defaultValue property="bracketThickness">2</defaultValue>

        <defaultValue property="enableGuideRing">false</defaultValue>

        <defaultValue property="ringColorCode">blue</defaultValue>

        <defaultValue property="ringThickness">2</defaultValue>

        <defaultValue property="ringStartAngleDeg">0.0</defaultValue>

        <defaultValue property="ringEndAngleDeg">360.0</defaultValue>

</initialization>

 

 

 

Adding the Guides to the Canvas (updating component.js)

 

 

Now comes the time for the rubber to meet the road.  We've brought properties into canvas components many timas already, but just to review at a high level; we'll need:

 

The local Proxies

me._enableArc = true;

 

 

//Part 8 Guide Lines

me._enableGuideLines = false;

me._enableGuideRing = false;

me._ringColorCode = 'blue';

me._ringThickness = 2;

me._bracketThickness = 2;

me._ringStartAngleDeg = 0.0;

me._ringEndAngleDeg = 360.0;

 

 

The new getter/setter functions:

me.enableArc = function(value) {

  if (value === undefined) {

  return me._enableArc;

  } else {

  me._enableArc = value;

  me.redraw();

  return me;

  }

};

 

 

// Part 8

me.enableGuideLines = function(value) {

  if (value === undefined) {

  return me._enableGuideLines;

  } else {

  me._enableGuideLines = value;

  me.redraw();

  return this;

  }

};

 

 

me.bracketThickness = function(value) {

  if (value === undefined) {

  return me._bracketThickness;

  } else {

  me._bracketThickness = value;

  me.redraw();

  return this;

  }

};

 

 

me.guideColorCode = function(value) {

  if (value === undefined) {

  return me._guideColorCode;

  } else {

  me._guideColorCode = value;

  me.redraw();

  return this;

  }

};

 

 

me.enableGuideRing = function(value) {

  if (value === undefined) {

  return me._enableGuideRing;

  } else {

  me._enableGuideRing = value;

  me.redraw();

  return this;

  }

};

 

 

me.ringColorCode = function(value) {

  if (value === undefined) {

  return me._ringColorCode;

  } else {

  me._ringColorCode = value;

  me.redraw();

  return this;

  }

};

 

 

me.ringThickness = function(value) {

  if (value === undefined) {

  return me._ringThickness;

  } else {

  me._ringThickness = value;

  me.redraw();

  return this;

  }

};

 

 

me.ringStartAngleDeg = function(value) {

  if (value === undefined) {

  return me._ringStartAngleDeg;

  } else {

  me._ringStartAngleDeg = value;

  me.redraw();

  return this;

  }

};

 

 

me.ringEndAngleDeg = function(value) {

  if (value === undefined) {

  return me._ringEndAngleDeg;

  } else {

  me._ringEndAngleDeg = value;

  me.redraw();

  return this;

  }

};

// End Part 8 Properties

 

 

 

Specific to the new feature, we'll need:

  • recalculateGuideRingAngles() moves into component.js and gets renamed me.recalculateGuideRingAngles()
  • The drawing code moves into the me.redraw() function and we'll refactor the variables to work with the existing code in component.js.  Variables from the html sandbox file that correspond to properties will get a "me._" prefix, width becomes me.$().width(), etc.
  • The newly migrated and refactored code blocks for drawing the guides get wrapped inside if statements and only executed when the designer has decided to enable them. 

 

 

The refactored recalculateGuideRingAngles () function:

//New with Part 8

me.recalculateGuideRingAngles = function(){

  //The ring has no max angle or measures, so it is trivial to recalculate.

  //Right now, this gauge is hardcoded to turn in a clockwise manner.

  //  Ensure that the arc can turn in a clockwise direction to get to the end angles

  while (me._ringEndAngleDeg < me._ringStartAngleDeg){

  me._ringEndAngleDeg = me._ringEndAngleDeg + 360.0;

  }

};

 

The me.redraw() function, after the newly refactored guide drawing code goes in:

//Part 8 - The guide lines

///////////////////////////////////////////

//Lets build a border ring around the gauge

///////////////////////////////////////////

if (me._enableGuideLines == true){

  var visRing = d3.select(myDiv).append("svg:svg").attr("width", "100%").attr("height", "100%");

 

  var ringOuterRad = me._outerRad + ( -1 * me._ringThickness);  //Outer ring starts at the outer radius of the inner arc

 

 

  var ringArcDefinition = d3.svg.arc()

  .innerRadius(me._outerRad)

  .outerRadius(ringOuterRad)

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

  .endAngle(me._ringEndAngleDeg * (pi/180)) //converting from degs to radians

 

 

  var ringArc = vis

  .append("path")

  .attr("d", ringArcDefinition)

  .attr("fill", me._ringColorCode)

  .attr("transform", "translate(" + me._offsetLeft + "," + me._offsetDown + ")");

}

///////////////////////////////////////////

//Lets build a the start and end lines

///////////////////////////////////////////

if (me._enableGuideRing == true){

  var visStartBracket = d3.select(myDiv).append("svg:svg").attr("width", "100%").attr("height", "100%");

  var lineData = [endPoints (me._outerRad, me._ringStartAngleDeg), {x:me._offsetLeft, y:me._offsetDown}, endPoints (me._outerRad, me._ringEndAngleDeg)];

  var lineFunction = d3.svg.line()

  .x(function(d) { return d.x; })

  .y(function(d) { return d.y; })

  .interpolate("linear");

 

  var borderLines = vis

  .attr("width", me.$().width()).attr("height", me.$().height()) // Added height and width so line is visible

  .append("path")

  .attr("d", lineFunction(lineData))

  .attr("stroke", me._ringColorCode)

  .attr("stroke-width", me._bracketThickness)

  .attr("fill", "none");

}

 

 

 

The Guide Lines and Arc in Action

 

 

PropertiesResult

Main Arc: Enabled

Radian Guide Lines: Enabled

Guide Arc: Enabled

Part8b.1.png

Main Arc: Disabled

Radian Guide Lines: Enabled

Guide Arc: Enabled

Part8b.4.png

Main Arc: Enabled

Radial Guide Lines: Disabled

Guide Arc: Enabled

Part8b.2.png

Main Arc: Enabled

Radian Guide Lines: Enabled

Guide Arc: Disabled

Part8b.3.png

 

 

As always, the extension project and test application are in a repository on Github.  Next, time, we'll start adding an indicator needle.

Design Studio SDK 1.6 speaks now Japanese

$
0
0

Since Design Studio 1.5 it is possible to design your dashboards for multi-language scenarios using the standard component TEXT_POOL alongside with the standard SAP backend localization features which work out of the box (e.g. date formats according to your user’s language settings). But what about SDK components? No worries we got something for you.

 

i18n and Localization

Localization and internationalization in terms of software is often abbreviated as i18n. SAP uses the term as well for their translation approach with their SAPUI5 library.

 

At first you need means to identify the user’s locale. You can do so using the method:


     this.sCurrentLocale = sap.ui.getCore().getConfiguration().getLanguage()

 

This method will return a language identifier (e.g. “de” for German) which again can be used to load the needed properties file containing the translations. Those files can be loaded as needed using SAP’s resources library exposed through jQuery. Please note it is not necessary to include the library as it is already loaded by default.


     jQuery.sap.resources({url : <path to default translation file>, locale: this.sCurrentLocale});

 

Of course there are many other libraries/plugins even jQuery based that support this localization mechanism as well. For this article I stick with the SAPUI5 implementation though.

 

Properties Files

The properties files need to contain key value pairs. A key identifying the property and a corresponding text. A file naming convention using SAP language codes as suffixes enables you to make your SDK properties and texts translatable (for example “i18n_ja.properties” for Japanese translations). You need to provide a default file from which the localized versions can be derived. In my case with regard to the example above the name would be “i18n.properties”.

 

Example of property file content:

 

     # SCN SDK community properties file to support text translations for our SDK components

     # Author: Martin Pankraz

     # Version 1.0 (21st of Feb 2016)

     Your_identifier=my translated value

 

How to provide the URL to the properties file to the SDK component? The easiest way is to add a property to your “contribution.xml” file of type Url. The Design Studio framework will make sure the access path is correct and offer you a file choosing dialog on design time. You need to choose your default file for the property and put all translations on the same folder! The library will load the translations by adding the suffix to the name of your default file. So you need to make sure the file naming is correct. Check the method to load translations again below.


     var oBundle = jQuery.sap.resources({url : <path to default translation file>, locale: this.sCurrentLocale});

 

The variable oBundle will contain the key value pairs provided in your loaded language file. You can access them using:


     oBundle.getText(“Your_identifier”);

 

Now you can assign this text to any of your texts on your SDK component and be ready to support multiple languages.

I would advise to make sure to supply default texts in English when no properties file is loaded for ease of use of the dashboard designer. In addition to that it would be nice to provide a small documentation so that the designer can learn what keys to use on the properties files to actually offer a new language other than English.

 

SAP Standard Texts

If you want to use SAP’s standard tools to offer translations for your SDK component you can use the property type “Text” on your “contribution.xml” file. In doing so this property will be translatable (check page 29 on the Developer Guide: Design Studio SDK 1.6).

 

Testing Localization

An easy way to test your SDK component localization implantation is to add an additional language to Internet Explorer (Intern options -> Language -> Add) and move it to the top of the list to make it your default locale. Now you can check if the corresponding properties file is loaded and your texts are translated as intended. For my first implementation I added Japanese and noticed that my i18n_ja.propties file was loaded successfully.

Make sure the character encoding supports the character set of your target language.

 

Final Words

To rely on SAP’s standard translation tools you should stick to the standard TEXT_POOL component and the SDK component property type “Text”. If that is not necessary the SAPUI5 resources library approach is also very easy, flexible and on top of it SAPUI5 compliant. For the community repository we haven’t decided yet how to approach this topic on a bigger scale. We have some ideas to integrate it with our shared components and modify our code generators so that newly generated community components are translatable out of the box. Modifying all components to use properties of type “Text” is also a possibility. You can check the coding for the SAPUI5 approach in action on our MultiComboBox component.

 

 

As always feel free to ask lots of follow up questions.

 

Yours

Martin


Jump Start ASUG Annual Conference SAPPHIRENOW with Design Studio Deep-Dive Pre-conference

$
0
0

1conf.jpg

ASUG Annual Conference pre-conference sessions provide in-depth sessions on various topics. ASUG Pre-conference day is Monday, May 16.

 

Here are the details:

Where: Orlando, Florida

When: Monday, May 16

 

For a complete list, see here.  I hope you join us for this hands-on session:

 

SAP BusinessObjects Design Studio Deep Dive (Advanced Topics)

SAP BusinessObjects Design Studio is SAP’s flagship product for the creation of dashboards and custom analytical applications. This full-day, hands-on workshop is a series of activities focused on SAP BusinessObjects Design Studio in combination with SAP BW powered by SAP HANA.

 

Attendees will learn to use the different components in SAP BusinessObjects Design Studio and how to go from simple KPI dashboards to more complex, self-service style applications.

 

Target Audience: BI Developer, BI Analyst, Dashboard Developer with basic knowledge and experience in SAP BusinessObjects Design Studio and SAP NetWeaver BW or SAP HANA.

 

Key Topics or Agenda Overview:

• Role of SAP BusinessObjects Design Studio in the SAP BusinessObjects BI portfolio

• SAP BusinessObjects Design Studio deployment options

• Creating a simple KPI dashboards

• Filtering your data using different options

• Using personalization and bookmarking

• Leveraging online composition as part of your dashboard

• Parallel process and background processing of data sources

• Using the Geo Map component to visualize geographic information

• Using Global Scripts

• Using the Scorecard component

• Using Profiling for your Dashboard

 

 

Key Outcomes or Benefits of Attending:

Attendees will learn to use the different components in SAP BusinessObjects Design Studio and how to go from simple KPI dashboards to more complex self-service style applications.

 

ASUG offered a Design Studio workshop at SAP TechEd last year and below are some of the comments received:

 

Very engaging and provided detailed solutions to any questions on Design Studio

Fantastic exercises and Ingo is an engaging presenter

Very interesting and helpful

Engaging presentation skills.

Learned a lot; great session.

Fantastic session, great to connect and use with Mobile BI - Ingo is a rock star presenter

 

Facilitator: Ingo Hilgefort SAP Mentor, Visual BI, ASUG Volunteer

 

ASUG has assembled a team of experts to help you answer your questions,  I hope you join us


Could this be you, attending the workshop?

pastte.jpg

Source: SAP TechEd Photographer



Register today

Your First Extension: Part 9a - Drawing an Indicator Needle (D3)

$
0
0

Now were ready to add an indicator needle.  In this installment, we'll return to our html sandbox and define our needle.  Next time, we'll migrate the changes into a component.  It will be a pointer, like you'd find in an analog car speedometer.  There are a few ways that we could add the needle:

 

  • We add an image and rotate it.  Either the developer would use a fixed image, or the designer would take an arbitrary image, define a centerpoint and use that.  The problem with raster images is that they can't scale properly with the rest of the component.
  • We add an SVG shape and rotate it.  This would work and we could then have the user specify an external SVG file, but dynamically modifying it afterwards is also very difficult.  Also editor to component workflows are not guaranteed to be simple.
  • We simply draw a fixed style of pointer in the component, using D3.  This is the least flexible way of doing it, but the simplest from a development ,designer usability and workflow perspective.  We'll choose this route and leave the file reference version as an exercise.

 

 

Needle Layout

 

 

We've already covered the tools needed to build our needle.  It will consist of two SVG paths: an arc and a coordinate based path.  We learned how to draw way back in Part 2a and have used that technique a few times since then.  We first learned to draw paths when we designed our padding visualizer in Part 4a and have also used this tenchnique since.  The path will be used to draw the needle and the arc will be used to draw the "base pin"; for those cases when the designer wants to put a circle at the axis of the needle, like you'd find in an analog automobile speedometer.  The pin - if present - will be drawn over the needle.  We'll use four coordinates to draw the needle.  The designer will have a few properties, with which she can specify the shape of the needle and base pin.

  • Needle Width: The width of the needle, in relative terms (as a %) to the size of the gauge.  So if it is set to 20%, then the needle's width will be 20% of whatever the radius of the gauge is; allowing it to scale.
  • Needle Tail Length: As a % of the gauge radius.  If this is positive, then the needle will be diamond shaped.  It could also be negative or zero (or not there at all).
  • Needle Length: As a % of the gauge radius.  Again, this could theoretically be zero or even negative (and point in the opposite direction), though I'm not sure what use case would call for this.
  • Diameter of the Base Pin: As a % of the gauge radius.  Using radius would be consistent with the format of the overall gauge, but we chose to define the width to be consistent with the width of the needle.  If the designer sets this to the same value as she set the needle width to, they will be the same size.

Part9a.2.png

 

 

We can easily define the waypoints for the needle's brush strokes:

 

XY
0needleHeadLength
needleWaypointOffset*0
0-(needleTailLength)
-(needleWaypointOffset*)0
0needleHeadLength

*needleWaypointOffset is a calculated property, needleWidth/2.

 

 

By combining these properties, brush stroke width and whether or not the shapes are filled, we can create a plethora of needle variants, with just a handful of properties.  Below is just a sample of what is possible.

Part9a.1.png

 

In order to do this, we'll follow the workflow below and keep a few things in mind.

  • The needle might not be drawn at all.  It should be optional.
  • If the needle is drawn, the base pin might or might not be drawn.  It should also be optional.
  • We'll need to apply a rotational transformation to the needle stroke waypoints, before drawing them.
  • Recall that to draw the stroke path, we're going to need an "accessor function" in the "d" property of the path. 
  • When/If we draw the base pin, we'll define the inner and outer radii based on whetrher or not we want to fill the pin.  If we don"t want to fill it, then the inner and outer radii need to line up with the inside and outside of the needle brushe strokes respectively.  If we do fill it in, then our inner radius will be zero.
  • If we don't fill in the base pin, then we'll need to have the option of not drawing the tail, or filling the needle in.  This way won't have the lines from the tail when we don't need or want them.
  • If we're not filling in the needle or base pin, then we'll want the option to only draw the "back" side of the pin arc: a semi-circle.  This combination can give us a wireframe version of the needle second from the left, above.
  • All lines and fills will be a single color, for the sake of simplicity.

Workflow.png

 

We've now laid out the logic workflow.  Since we're not breaking new ground in terms of technique, we'll simply post  the sandbox html file.  The needle and base pin as created between lines 120 and 230.  Next time, we'll refactor this new code into a component.

 

<!DOCTYPE html>

<html>

  <head>

  <meta http-equiv='X-UA-Compatible' content='IE=edge' />

  <title>Part 5</title>

 

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

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

  <!-- <script src="file://d3/d3.js" charset="utf-8"></script>-->

  <script>

  var vis = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

 

  var pi = Math.PI;

 

  //Viz definitiions

  var innerRad = 0;

  //var outerRad = 70;

  var width = 200;

  var height = 200;

  var startAngleDeg = -45;

  var endAngleDeg = 45;

  var colorCode = "red";

 

 

  //Outer Dimensions & Positioning

  var paddingTop = 10;

  var paddingBottom = 10;

  var paddingLeft = 10;

  var paddingRight = 10;

 

  //The total size of the component is calculated from its parts

 

  // Find the larger left/right padding

  var lrPadding = paddingLeft + paddingRight;

  var tbPadding = paddingTop + paddingBottom;

  var maxPadding = lrPadding;

  if (maxPadding < tbPadding){

  maxPadding = tbPadding

  }

 

  var outerRad = (width - 2*(maxPadding))/2;

  //var width = (outerRad * 2) + paddingLeft + paddingRight;

  //var height = (outerRad * 2) + paddingTop + paddingBottom;

 

  //The offset will determine where the center of the arc shall be

  var offsetLeft = outerRad + paddingLeft;

  var offsetDown = outerRad + paddingTop;

 

  //Don't let the arc have a negative length

  if (endAngleDeg < startAngleDeg){

  endAngleDeg = startAngleDeg;

  alert("End angle may not be less than start angle!");

  }

 

  var arcDef = d3.svg.arc()

  .innerRadius(innerRad)

  .outerRadius(outerRad)

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

  .endAngle(endAngleDeg * (pi/180)); //converting from degs to radians

 

  var guageArc = vis.append("path")

     .style("fill", colorCode)

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

     .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")")

     .attr("d", arcDef);

 

  ///////////////////////////////////////////

  //Lets build a border ring around the gauge

  ///////////////////////////////////////////

  //var visRing = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

  var ringThickness = 2;

  var ringOuterRad = outerRad + ringThickness;  //Outer ring starts at the outer radius of the inner arc

  var ringColorCode = "black";

  var ringStartAngleDeg = 0;

  var ringEndAngleDeg = 360;

 

  //Don't let the arc have a negative length

  if (ringEndAngleDeg < ringStartAngleDeg){

  ringEndAngleDeg = ringStartAngleDeg;

  alert("End angle of outer ring may not be less than start angle!");

  }

  var ringArcDefinition = d3.svg.arc()

  .innerRadius(outerRad)

  .outerRadius(ringOuterRad)

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

  .endAngle(ringEndAngleDeg * (pi/180)) //converting from degs to radians

 

  var ringArc = vis

  .append("path")

  .attr("d", ringArcDefinition)

  .attr("fill", ringColorCode)

  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");

 

 

 

  ///////////////////////////////////////////

  //Lets build a the start and end lines

  ///////////////////////////////////////////

  var bracketThickness = 2;

  var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];

  var visStartBracket = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");

  var lineFunction = d3.svg.line()

  .x(function(d) { return d.x; })

  .y(function(d) { return d.y; })

  .interpolate("linear");

 

  var borderLines = vis

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

  .append("path")

  .attr("d", lineFunction(lineData))

  .attr("stroke", ringColorCode)

  .attr("stroke-width", bracketThickness)

  .attr("fill", "none");

 

  //Helper function

  function endPoints (lineLength, lineAngle){

  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));

  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));

  return {x:endX, y:endY}

  }

 

  ///////////////////////////////////////////

  //Lets add the indicator needle

  ///////////////////////////////////////////

 

  var needleColorCode = "black";

 

  var enableIndicatorNeedle = true;

  var enableIndicatorNeedleTail = true;

  var needleWidth = 20;

  var needleHeadLength = 100;

  var needleTailLength = 0;

  var needleLineThickness = 2;

  var fillNeedle = true;

 

 

 

  if (enableIndicatorNeedle == true){

  var needleWaypointOffset = needleWidth/2;

 

  //needleWaypoints is defined with positive y axis being up

  //The initial definition of needleWaypoints is for a full diamond, but if enableIndicatorNeedleTail is false, we'll abbreviate to a chevron

  var needleWaypoints = [{x: 0,y: needleHeadLength}, {x: needleWaypointOffset,y: 0}, {x: 0,y: (-1*needleTailLength)}, {x: (-1*needleWaypointOffset),y: 0}, {x: 0,y: needleHeadLength}]

  if (enableIndicatorNeedleTail == false){

  if (fillNeedle == false){

  //If we have no tail and no fill then there is no need to close the shape.

  //Leave it as an open chevron

  needleWaypoints = [{x: needleWaypointOffset,y: 0}, {x: 0,y: needleHeadLength}, {x: (-1*needleWaypointOffset),y: 0}];

  }

  else {

  //There is no tail, but we are filling the needle.

  //In this case, draw it as a triangle

  needleWaypoints = [{x: 0,y: needleHeadLength}, {x: needleWaypointOffset,y: 0}, {x: (-1*needleWaypointOffset),y: 0}, {x: 0,y: needleHeadLength}]

  }

 

 

  }

 

  //we need to invert the y-axis and scale the indicator to the gauge.

  //  If Y = 100, then that is 100% of outer radius.  So of Y = 100 and outerRad = 70, then the scaled Y will be 70.

  var needleFunction = d3.svg.line()

  .x(function(d) { return (d.x)*(outerRad/100); })

  .y(function(d) { return -1*(d.y)*(outerRad/100); })

  .interpolate("linear");

 

  //Draw the needle, either filling it in, or not

  var needleFillColorCode = needleColorCode;

  if (fillNeedle == false){

  needleFillColorCode = "none";

  }

 

  //Draw the needle

  var needle = vis

  .append("g")

  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")")

  .append("path")

  .attr("class", "tri")

  .attr("d", needleFunction(needleWaypoints))

  .attr("stroke", needleColorCode)

  .attr("stroke-width", needleLineThickness)

  .attr("fill", needleFillColorCode);

 

 

 

  //Arcs are in radians, but rotation transformations are in degrees.  Kudos to D3 for consistency

  needle.attr("transform", "rotate(" + endAngleDeg + ")");

  }

 

 

  ///////////////////////////////////////////

  //Lets add a needle base pin

  ///////////////////////////////////////////

 

 

  var enableIndicatorNeedleBase = true;

  var fullBasePinRing = true;

  var fillNeedlaBasePin = true;

  var needleBaseDiameter = 20;

 

  if (enableIndicatorNeedleBase == true){

  // Like the rest of the needle, the size of the pin is defined relative to the main arc, as a % value

  var needleIBasennerRadius = (needleBaseDiameter/2)*(outerRad/100) - (needleLineThickness/2);

  var needleBaseOuterRadius = needleIBasennerRadius + needleLineThickness;

  if (fillNeedlaBasePin == true){

  needleIBasennerRadius = 0.0;

  }

 

 

  // The pin will either be a 180 degree arc, or a 360 degree ring; starting from the 9 O'clock position.

  var needleBaseStartAngle = 90.0;

  var needleBaseEndAngle = 270.0;

  if (fullBasePinRing == true){

  needleBaseEndAngle = 450.0;

  }

 

 

  //Don't let the arc have a negative length

  if (needleBaseEndAngle < needleBaseStartAngle){

  needleBaseEndAngle = needleBaseStartAngle;

  alert("End angle of outer ring may not be less than start angle!");

  }

 

  //Transform the pin ring

  var nbTransformedStartAngle = needleBaseStartAngle + endAngleDeg;

  var nbTransformedEndAngle = needleBaseEndAngle + endAngleDeg;

 

 

  var pinArcDefinition = d3.svg.arc()

  .innerRadius(needleIBasennerRadius)

  .outerRadius(needleBaseOuterRadius)

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

  .endAngle(nbTransformedEndAngle * (pi/180)) //converting from degs to radians

 

  var pinArc = vis

  .append("path")

  .attr("d", pinArcDefinition)

  .attr("fill", needleColorCode)

  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");

  }

 

 

  </script>

    </head>

  <body class='sapUiBody'>

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

  </body>

</html>

Design Studio 1.6 - Using Multiselect in Scorecard

$
0
0

If you want to use the multiselect in scorecard, this blog is for you.

 

The way to do

In scorecard, when selection type is "Multi", then you are allowed to chose more rows at one time (CTRL, SHIFT buttons work then). The information will be passed to the scripting, there you can use "getSelection()" method to read it out.

 

The implementation

Using script, the method is giving you for every dimension all selected members (in case you use CTRL only one is given, by SHIFT more than one).

 

The restriction

The API is giving you the dimensions INDEPENDENTLY, therefore the multi selection is basically working only when you have only one dimension in your row scope.

Why? Via the current API it is not possible to readout the dependencies between members of more than one dimension.

 

How you can read it out?

/assumption: only one dimension in row scope/ then the selection is toggled and can be on or off. You need one global variable on in scripting as you need to make a code which works like a semafor:

 

* global variable does not have a member -> selection

* global variable already has the member -> de-selection.

 

Script

var

colSel = SCORECARD_1DIM.getSelectedColumnId();
var sel = SCORECARD_1DIM.getSelection();
sel.forEach(function(value, key) {  if(key == "Product" && SELECTION_Product == "") {  SELECTION_Product = key + "|;";  }  value.forEach(function(element, index) {  if(key == "Product") {   if(SELECTION_Product.indexOf(";" + element + ";") > -1) {    // the element is now unselected!  SELECTION_Product =   SELECTION_Product.substring(    0,  SELECTION_Product.indexOf(element + ";"))  +  SELECTION_Product.substring(  SELECTION_Product.indexOf(element + ";") + (element + ";").length  );      } else {        SELECTION_Product = SELECTION_Product + element + ";";      }    }  });
});
TEXT_2.setText(SELECTION_Product);
asas

As you can see, there is a simple check and modification of the selection string.

 

I use the ";" character in the beginning to avoid that the IF check in line 12 will be positive in case there is a key which is a substring of other key.. by this, later you need to ignore the first member after split as it is empty.

 

How to readout the real selection?

now, having this string, you can simply split it (ignoring first member):

var list = SELECTION_Product.split(";");
var listOut = "";
list.forEach(function(element, index) {  if(element != "") {      listOut = listOut + " | " + element;  }
});
TEXT_2.setText(listOut);

In the place where I create the second output (line 5) you can make your code.

 

Application

In the given example, you can find 2 scorecards, the first one (_1DIM) has single dimension, as in this example. The second one (_2DIM) was a try to get out the selection with 2 dimensions - this failed for today... perhaps some other day I can find a script which makes it...

 

Download

The app can be downloaded here

applications/SCORECARD_SELECTION_MULTI-20160222223057.zip at master · org-scn-design-studio-community/applications · Git…

 

Does anyone need an exact selection of combinations of 2 or more dimensions? to make it work, probably the selection API needs to be changed like "getRows()" and then "getDimensionKey("A")", etc.

 

feel free to comment and ask

Your First Extension: Part 9b - Drawing an Indicator Needle in the Component

$
0
0

In the last instalment, we investigated adding a two component indicator needle to our gauge; at least in the sandbox html file.  Now we're going to update the component.  As usual, the process follows the following steps:

  1. Determine which variables from our sandbox code need to become properties (either visible in the properties pane or hidden) and which can remain as local variables in the component.js file.
  2. Add those properties to contribution.xml
  3. If we are adding a group to the properties pane, we need to declare it in contribution.xml and make sure that the relevant properties get added to it.  To keep our properties pane organized, we be adding a new "Indicator Needle" group and all of the new properties will go there.
  4. Create getters and setters for those properties
  5. Create ztl functions for anything that we're adding to the Design Studio script API.  In this case, we'll leave the properties as design time only.  Conceivably, we could make the needle and base pin shapes flexible and capable of being manipulated via scripting, but the use case is not compelling enough to add the additional complexity to the tutorial.
  6. The actual drawing code gets copied over and refactored to use the property variables, where applicable.

 

We added  large number of variables in Part 9a.  Of these, a dozen make sense as properties.  We also won't want to leave any of these properties uninitialized.

 

 

 

Contribution.xml

 

In the contribution.xml file, we're adding the "Indicator Needle" group, the properties in the table above and their initialization values.

 

PropertyTypeDefault Value
enableIndicatorNeedlebooleanfalse
enableIndicatorNeedleTailbooleanfalse
fillNeedlebooleanfalse
needleColorCodestring'black'
needleWidthint10
needleHeadLengthint100
needleTailLengthint10
needleLineThicknessint2
enableIndicatorNeedleBasebooleanfalse
fullBasePinRingbooleanfalse
fillNeedlaBasePinbooleanfalse
needleBaseRadiusbooleanfalse

 

 

The new group declaration:

  <group

  id="SCNGaugeNeedleSettings"

  title="Indicator Needle"

  tooltip="Gauge Indicator Needle Settings"/>

 

The property declarations:

<property id="enableIndicatorNeedle" title="Enable Indicator Needle" type="boolean" group="SCNGaugeNeedleSettings"/>

<property id="enableIndicatorNeedleTail"

  title="Enable Indicator Needle Tail"

  type="boolean"

  tooltip="Enable the tail on the the indicator needle and make it a diamond"

  group="SCNGaugeNeedleSettings"/>

<property id="fillNeedle"

  title="Fill Indicator Needle"

  type="boolean"

  tooltip="Enable color fill on the indicator needle"

  group="SCNGaugeNeedleSettings"/>

<property id="needleColorCode"

  title="Needle Color"

  type="Color"

  tooltip="Needle Color (outline and fill of indicator needle and base pin)"

  group="SCNGaugeNeedleSettings"/>

<property id="needleWidth"

  title="Indicator Needle Width"

  type="int"

  tooltip="Base width of the indicator needle, as a percentage of the gauge radius"

  group="SCNGaugeNeedleSettings"/>

<property id="needleHeadLength"

  title="Indicator Needle Length"

  type="int"

  tooltip="Length of the indicator needle, as a percentage of the gauge radius"

  group="SCNGaugeNeedleSettings">

  <!-- <possibleValue>1</possibleValue>  -->

</property>

<property id="needleTailLength"

  title="Indicator Needle Tail Length"

  type="int"

  tooltip="Tail Length of the indicator needle, as a percentage of the gauge radius"

  group="SCNGaugeNeedleSettings"/>

<property id="needleLineThickness"

  title="Indicator Line Thickness"

  type="int"

  tooltip="Thickness of the lines used to draw the indicator needle and base pin"

  group="SCNGaugeNeedleSettings"/>

<property id="enableIndicatorNeedleBase"

  title="Enable Base Pin"

  type="boolean"

  tooltip="Enable the base pin (circle) on the indicator needle"

  group="SCNGaugeNeedleSettings"/>

<property id="fullBasePinRing"

  title="360° Base Pin"

  type="boolean"

  tooltip="Enable a full 360 degree base pin circle.  Disabling this results in a 180 degree arc on the needle tail"

  group="SCNGaugeNeedleSettings"/>

<property id="fillNeedlaBasePin"

  title="Fill Base Pin"

  type="boolean"

  tooltip="Fill the base pin, with the indicator needle fill color"

  group="SCNGaugeNeedleSettings"/>

<property id="needleBaseWidth"

  title="Base Pin Width"

  type="int"

  tooltip="Diameter (as a % of main arc radius) of the base pin"

  group="SCNGaugeNeedleSettings"/>

 

The new initialization default values:

<initialization>

        …

        <defaultValue property="enableIndicatorNeedle">false</defaultValue>

        <defaultValue property="enableIndicatorNeedleTail">false</defaultValue>

        <defaultValue property="fillNeedle">false</defaultValue>

        <defaultValue property="needleColorCode">black</defaultValue>

        <defaultValue property="needleWidth">10</defaultValue>

        <defaultValue property="needleHeadLength">100</defaultValue>

        <defaultValue property="needleTailLength">10</defaultValue>

        <defaultValue property="needleLineThickness">2</defaultValue>

        <defaultValue property="enableIndicatorNeedleBase">false</defaultValue>

        <defaultValue property="fullBasePinRing">false</defaultValue>

        <defaultValue property="fillNeedlaBasePin">false</defaultValue>

        <defaultValue property="needleBaseWidth">20</defaultValue>

</initialization>

 

 

 

Property Proxies and Getter/Setters

 

As usual, our property proxy values ( the "me._" variables) get declared at the head of the component.js file.

me._enableIndicatorNeedle = false;

me._enableIndicatorNeedleTail = false;

me._fillNeedle = false;

me._needleColorCode = 'black';

me._needleWidth = 10;

me._needleHeadLength = 100;

me._needleTailLength = 10;

me._needleLineThickness = 2;

me._enableIndicatorNeedleBase = false;

me._fullBasePinRing = false;

me._fillNeedlaBasePin = false;

me._needleBaseRadius = false;

 

And each of these will need a getter/setter for property synchronization to work.  Don't forget that all of these properties affect display and if any is changed, we need to trigger a redraw:

//Step 9

me.enableIndicatorNeedle = function(value) {

  if (value === undefined) {

  return me._enableIndicatorNeedle;

  } else {

  me._enableIndicatorNeedle = value;

  me.redraw();

  return me;

  }

};

 

 

me.enableIndicatorNeedleTail = function(value) {

  if (value === undefined) {

  return me._enableIndicatorNeedleTail;

  } else {

  me._enableIndicatorNeedleTail = value;

  me.redraw();

  return me;

  }

};

 

 

me.fillNeedle = function(value) {

  if (value === undefined) {

  return me._fillNeedle;

  } else {

  me._fillNeedle = value;

  me.redraw();

  return me;

  }

};

 

 

me.needleColorCode = function(value) {

  if (value === undefined) {

  return me._needleColorCode;

  } else {

  me._needleColorCode = value;

  me.redraw();

  return me;

  }

};

 

 

me.needleWidth = function(value) {

  if (value === undefined) {

  return me._needleWidth;

  } else {

  me._needleWidth = value;

  me.redraw();

  return me;

  }

};

 

 

me.needleHeadLength = function(value) {

  if (value === undefined) {

  return me._needleHeadLength;

  } else {

  me._needleHeadLength = value;

  me.redraw();

  return me;

  }

};

 

 

me.needleTailLength = function(value) {

  if (value === undefined) {

  return me._needleTailLength;

  } else {

  me._needleTailLength = value;

  me.redraw();

  return me;

  }

};

 

 

me.needleLineThickness = function(value) {

  if (value === undefined) {

  return me._needleLineThickness;

  } else {

  me._needleLineThickness = value;

  me.redraw();

  return me;

  }

};

 

 

me.enableIndicatorNeedleBase = function(value) {

  if (value === undefined) {

  return me._enableIndicatorNeedleBase;

  } else {

  me._enableIndicatorNeedleBase = value;

  me.redraw();

  return me;

  }

};

 

 

me.fullBasePinRing = function(value) {

  if (value === undefined) {

  return me._fullBasePinRing;

  } else {

  me._fullBasePinRing = value;

  me.redraw();

  return me;

  }

};

 

 

me.fillNeedlaBasePin = function(value) {

  if (value === undefined) {

  return me._fillNeedlaBasePin;

  } else {

  me._fillNeedlaBasePin = value;

  me.redraw();

  return me;

  }

};

 

 

me.needleBaseRadius = function(value) {

  if (value === undefined) {

  return me._needleBaseRadius;

  } else {

  me._needleBaseRadius = value;

  me.redraw();

  return me;

  }

};

 

 

 

The Drawing Code

 

 

Once we have everything else in place, we can copy/paste the 110 lines of drawing code to componet.js's redraw() function and refactor the variables that are properties to use the proxy values.

///////////////////////////////////////////

//Lets add the indicator needle

///////////////////////////////////////////

 

 

if (me._enableIndicatorNeedle == true){

  var needleWaypointOffset = me._needleWidth/2;

 

 

  //needleWaypoints is defined with positive y axis being up

  //The initial definition of needleWaypoints is for a full diamond, but if me._enableIndicatorNeedleTail is false, we'll abbreviate to a chevron

  var needleWaypoints = [{x: 0,y: me._needleHeadLength}, {x: needleWaypointOffset,y: 0}, {x: 0,y: (-1*me._needleTailLength)}, {x: (-1*needleWaypointOffset),y: 0}, {x: 0,y: me._needleHeadLength}]

  if (me._enableIndicatorNeedleTail == false){

  if (me._fillNeedle == false){

  //If we have no tail and no fill then there is no need to close the shape.

  //Leave it as an open chevron

  needleWaypoints = [{x: needleWaypointOffset,y: 0}, {x: 0,y: me._needleHeadLength}, {x: (-1*needleWaypointOffset),y: 0}];

  }

  else {

  //There is no tail, but we are filling the needle.

  //In this case, draw it as a triangle

  needleWaypoints = [{x: 0,y: me._needleHeadLength}, {x: needleWaypointOffset,y: 0}, {x: (-1*needleWaypointOffset),y: 0}, {x: 0,y: me._needleHeadLength}]

  }

 

 

  }

 

 

  //we need to invert the y-axis and scale the indicator to the gauge.

  //  If Y = 100, then that is 100% of outer radius.  So of Y = 100 and outerRad = 70, then the scaled Y will be 70.

  var needleFunction = d3.svg.line()

  .x(function(d) { return (d.x)*(outerRad/100); })

  .y(function(d) { return -1*(d.y)*(outerRad/100); })

  .interpolate("linear");

 

 

  //Draw the needle, either filling it in, or not

  var needleFillColorCode = me._needleColorCode;

  if (me._fillNeedle == false){

  needleFillColorCode = "none";

  }

 

  //Draw the needle

  var needle = vis

  .append("g")

  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")")

  .append("path")

  .attr("class", "tri")

  .attr("d", needleFunction(needleWaypoints))

  .attr("stroke", me._needleColorCode)

  .attr("stroke-width", me._needleLineThickness)

  .attr("fill", needleFillColorCode);

 

 

 

 

  //Arcs are in radians, but rotation transformations are in degrees.  Kudos to D3 for consistency

  needle.attr("transform", "rotate(" + endAngleDeg + ")");

}

 

 

 

 

///////////////////////////////////////////

//Lets add a needle base pin

///////////////////////////////////////////

 

 

var me._enableIndicatorNeedleBase = true;

var me._fullBasePinRing = true;

var me._fillNeedlaBasePin = true;

var me._needleBaseRadius = 20;

 

 

if (me._enableIndicatorNeedleBase == true){

  // Like the rest of the needle, the size of the pin is defined relative to the main arc, as a % value

  var needleIBasennerRadius = (me._needleBaseRadius/2)*(outerRad/100) - (me._needleLineThickness/2);

  var needleBaseOuterRadius = needleIBasennerRadius + me._needleLineThickness;

  if (me._fillNeedlaBasePin == true){

  needleIBasennerRadius = 0.0;

  }

 

 

 

  // The pin will either be a 180 degree arc, or a 360 degree ring; starting from the 9 O'clock position.

  var needleBaseStartAngle = 90.0;

  var needleBaseEndAngle = 270.0;

  if (me._fullBasePinRing == true){

  needleBaseEndAngle = 450.0;

  }

 

 

  //Don't let the arc have a negative length

  if (needleBaseEndAngle < needleBaseStartAngle){

  needleBaseEndAngle = needleBaseStartAngle;

  alert("End angle of outer ring may not be less than start angle!");

  }

 

 

  //Transform the pin ring

  var nbTransformedStartAngle = needleBaseStartAngle + endAngleDeg;

  var nbTransformedEndAngle = needleBaseEndAngle + endAngleDeg;

 

 

  var pinArcDefinition = d3.svg.arc()

  .innerRadius(needleIBasennerRadius)

  .outerRadius(needleBaseOuterRadius)

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

  .endAngle(nbTransformedEndAngle * (pi/180)) //converting from degs to radians

 

 

  var pinArc = vis

  .append("path")

  .attr("d", pinArcDefinition)

  .attr("fill", me._needleColorCode)

  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");

}

 

 

Once these go into place, you can use the properties pane to enable the indicator needle and configure it.

Part9b.1.png

 

Screen Shot 2016-02-19 at 15.09.13.pngScreen Shot 2016-02-19 at 15.07.55.pngScreen Shot 2016-02-19 at 15.07.29.png
Screen Shot 2016-02-19 at 15.10.08.pngScreen Shot 2016-02-19 at 15.10.46.pngScreen Shot 2016-02-19 at 15.12.06.png
Screen Shot 2016-02-19 at 15.11.14.pngScreen Shot 2016-02-19 at 15.11.37.pngScreen Shot 2016-02-19 at 15.13.04.png

 

As usual, the current state of the component and a test app are in a Github repository.

ASUG BI Webinar List - March 2016

$
0
0

Here the updated list of webinars for January 2016 (Happy New Year to everyone)

 


For all webinars :

 

  • Start Time : 11:00 AM (CT), 12:00 PM (ET), 10:00 AM (MT), 9:00 AM (PT)
  • Duration : 1 hour

 

 

 

In this session attendees will learn how a company could leverage SAP BusinessObjects Design Studio as an environment to create a self-service BI solution. Attendees will learn about the steps that are needed to create a self-service environment and where the current limitations are by using SAP BusinessObjects Design Studio. This presentation will focus on outlining which components are required to create the self-service BI solution and outline the required steps to create the application.

 

 

March 02 - SAP S/4HANA Analytics & SAP BW Data Integration Overview

 

Real-time operational reporting in the context of SAP S/4HANA

Positioning with SAP BW powered by SAP HANA

 

* Integration-enabling capabilities in SAP BW powered by SAP HANA

* User scenarios

* Possible Architectures

* Integration Scenarios

* Summary and additional information.

 

 

March 03 - SAP HANA Accelerators: Making the Side Car Your Main Car

 

SAP HANA accelerators are an extremely powerful and cost effective way of increasing a customer’s investment in SAP HANA. With two flavors - ERP accelerators and business application accelerators - a multitude of transactions, interfaces, and reports can be enhanced to reduce database access times to near zero. See how numerous customers maximize these functions and provide results that include lower database usage, improved end user satisfaction, and increased productivity.

 

 

March 07 - SAP Predictive Analytics - Integration with SAP HANA


This session explains how SAP Predictive Analytics for SAP HANA adds value in terms of business investments, guides you through the steps required to integrate SAP Predictive Analytics with SAP HANA and describes how you can automate the predictive process on SAP HANA using APL (Automated Predictive Library).

 

The demo scenario will show you how you can call the APL functions within SAP Predictive Analytics to generate in-database training and scoring.

 

 

March 08 - Design Studio Ready-to-Run Application & Templates

 

Agenda Draft:

-Short Introduction (10 min)

-        Generic Analysis Template (Example 1: how to modify SAP template and save as custom template)

-        Online Composition

-        Lumira Template

-        Layout templates

-        Planning Template

-        Example 2: How to use layout template

-        Example 3 : How to create a KPI Template

-        Q&A

-        Roadmap next release

 

 

March 15 - Focus on BI/Analytics - Top 10 Ways to Squeeze More Value from Your SAP BI/Analytics Investments (Live and Interactive)

 

On your journey with SAP’s BI/Analytics solutions you are continually looking for opportunities to gain more business value from your SAP investments, while reducing your total cost of ownership. It does not matter whether your industry is growing rapidly, or facing challenges, everyone has this as a big topic of interest.

 

During this session we will share with our top ten ways that you can do to drive more value from your SAP BI/Analytics investments today and in the future. We will also give you the opportunity to interact with us to ask questions and share your thoughts.

 

Please join us and come ready to learn what you can do to squeeze more value from your SAP BI/Analytics Investments. You will leave this session with a number of key takeaways to put into action in your organization.

 

 

March 16 - Geospatial Analysis with SAP Lumira


Join us to understand the geospatial capabilities in SAP Lumira to enrich data with geo-coding, build geographic hierarchy and build geo maps visualizations. along with roadmap for geospatial analysis with SAP Lumira.

 

 

March 17 - Shhhhhh, Listen - Your Machine is Trying to Tell You Something You Don't Know


When manual reporting using Excel proved to be impractical for reporting machine failure level hourly for a global corporation, management embarked on a journey to create an infrastructure that saved not only time for already busy users, but transformed data into actionable, constant flow, and performance change reporting. A data warehouse was created to hold past and present machine data for robust reporting. In addition, a self-contained and robust Extraction, Transformation, and Loading (ETL) process was created to convert transactional data into an hourly performance measurement tool. The company is now able to set targets and measure how machines perform against pre-defined and constantly re-evaluated targets by region, county, and plant all the way down to machine level. The drill down functionality allows KPIs to become meaningful on a daily and even hourly basis and ad-hoc analysis is now possible. The result - one common platform and one insightful global company.

 

 

March 22 - Customer Story: Manufacturing Analytical Self Service User Experience with SAP Hana and Lumira with Albemarle

 

Learn how Albemarle is improving the Manufacturing Analytical Self Service User Experiences using SAP technologies such as HANA, Fiori, and Lumira as well as innovative and advanced data, analytical and Cloud solutions

 

 

March 23 - Simplified: Tools for Migrating Your SAP BW System to SAP HANA

 

Learn about the SAP BW Migration Cockpit and the tools it combines to make the migration of an existing SAP BW deployment to the SAP HANA platform a more beautiful experience. See a demo of the Migration Cockpit and the most important tools and hear how they support a smooth transition to SAP HANA.  Hear firsthand how to find potential ABAP performance issues in custom coded BW transformation routines.

 

 

March 28 - What's New in SAP Lumira 1.30

 

Join the session for an overview of SAP Lumira (desktop,sServer for team, and BI platform), what’s new in Lumira 1.30 and future roadmap.

 

 

March 29 - The Benefits of the SAP BI Portfolio for S/4HANA Customers


SAP S/4HANA customers should consider BI applications in addition to their embedded analytics to cover all their use cases.

 

 

March 30 - Handle Big Data with SAP BW 7.5 and Hadoop


Experience how release 7.5 of SAP Business Warehouse (SAP BW) powered by SAP HANA supports integration with Apache Hadoop. The session shows how you can combine existing enterprise data in SAP BW with Big Data in Apache Hadoop file systems.

 

 

March 31 - SAP HANA DW Modeling Approaches


This session introduces new functionality of SAP Business Warehouse (SAP BW) and SAP HANA for building agile, large-scale data warehouses. Selected examples of hybrid data warehouse architectures illustrate how you can maximize business benefits by combining modeling options available for SAP BW and SAP HANA. The session concludes with a discussion on how to model modern, data warehouses with SAP BW and SAP HANA in an SAP S/4HANA context.

 

 

 


I hope you enjoy these session.

 

Please note, that these are webinars organized by the ASUG BI group and for attending you need to be a ASUG Member.

Viewing all 662 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>