This Article describes an easy way to create small indicator graphics. In a recent project an interactive report was implemented showing 8 numerical values and some metadata for each dataset. It was extremely difficult for the enduser to decide whether this dataset was ok or needs some further attention. After translating these values into some sort of graphical indicator, the semantic interpretation of the dataset could now take place immediately. The result of the described example below will look like: 

In this example, I will only use two values so it is much more understandable. The data comes straight from the demo EMP table - so the first step is to create an interactive report showing all data from this table.

Now, we need a new function, which is responsible to create our SVG containing the indicator graphic. Lets have a look at the function-header first:

create or replace function "DRAW_INDICATOR_SVG" (
    p_svg_id        in NUMBER
  p_value_top     in NUMBER
  p_value_bottom  in NUMBER
  p_width         in NUMBER default 80
  p_height        in NUMBER default 12
  p_percent_span  in NUMBER default 110)
return CLOB 
  • p_svg_id is any ID that the SVG will get - this might be useful later (e.g. for Dynamic Actions )
  • p_value_top is a value between -1 and 1 and will be shown on the top half of the indicator
  • p_value_bottom is a value between -1 and 1 and will be shown on the bottom half of the indicator
  • p_width is half the width - the size of the resulting SVG will be p_width * 2
  • p_height is half the height
  • p_percent_span - means the indicator goes from -110% to 110% - or a value of -1.1 to 1.1 - so it can be used for scaling

 

The functions main code is shown next (sorry for the long lines, but i tend to fully use the available space on the screen :-) ):

create or replace function "DRAW_INDICATOR_SVG" (
    p_svg_id        in NUMBER
  p_value_top     in NUMBER
  p_value_bottom  in NUMBER
  p_width         in NUMBER default 80
  p_height        in NUMBER default 12
  p_percent_span  in NUMBER default 110)
return CLOB
is
    l_svg           CLOB;
    l_pos           NUMBER;
    l_color         VARCHAR2(100);
    l_triagle_width NUMBER := round(p_height/2.5);
    
begin
    
    -- open SVG
    l_svg := l_svg || '<svg id="scg_indicator_'
                   || p_svg_id
                   || '" width="' 
                   || (p_width*2
                   || '" height="' 
                   || (p_height*2
                   || '">';

    -- indicator top
    l_pos := round(p_width (p_width p_value_top 100 p_percent_span));
    if p_value_top 
    then l_color := 'red'
    else if p_value_top 
        
then l_color := 'green'
         else l_color := 'gray'
         end if
    end if;

    l_svg := l_svg || '<path d="M '
                  
||l_pos||' '||p_height
                   ||' L '||(l_pos-l_triagle_width)||' 0'
                   ||' L '
||(l_pos+l_triagle_width)||' 0'
                   ||' Z" stroke="'
||l_color
                   ||'" fill="'||l_color
                   ||'" class="line_bar"'
                   ||' title="Top: '||round(p_value_top*100)||'%" />';
    if p_height 15 
    then l_svg := l_svg || '<text x="'||(l_pos-4)||'" y="'||(10)
                        ||'" font-family="Arial" font-size="11"'
                        ||' fill="white" title="Top: '||round(p_value_top*100)
                        ||'%" >T</text>'
    end if;

    -- indicator bottom
    l_pos := round(p_width (p_width p_value_bottom 100 p_percent_span));
    if p_value_bottom 
    then l_color := 'red'
    else if p_value_bottom 
        
then l_color := 'green'
         else l_color := 'gray'
         end if
   
end if;

    l_svg := l_svg || '<path d="M '
                   ||l_pos||' '||p_height
                  
||' L '||(l_pos-l_triagle_width)||' '||(p_height*2)
                  
||' L '||(l_pos+l_triagle_width)||' '||(p_height*2)
                   ||' Z" stroke="'||l_color
                   ||'" fill="'||l_color
                  
||'" class="line_bar"'
                   ||' title="Bottom: '||round(p_value_bottom*100)||'%" />';
    if p_height 15 
    then l_svg := l_svg || '<text x="'||(l_pos-4)||'" y="'||(p_height*2-2)
                        ||'" font-family="Arial" font-size="11"'
                        ||' fill="white" title="Bottom: '
||round(p_value_bottom*100)
                       
||'%" >B</text>'
   
end if;

    -- close SVG
    l_svg := l_svg || '<path d="M 0 '||p_height
                   ||' L '||(p_width*2)||' '||p_height
                  
||'" stroke="#AAAAAA" stroke-width="1" class="line_bar"/>';
    l_svg := l_svg || '<path d="M '||p_width||' 0'
                   ||' L '
||p_width||' '||(p_height*2)
                   ||'" stroke="#AAAAAA" stroke-width="1" class="line_bar"/>';
    if p_height <= 15 then
      l_svg := l_svg || '<text x="0" y="'||(p_height-2)
                    
||'" font-family="Arial" font-size="9"'
                     ||' fill="black" >T</text>'
;
      l_svg := l_svg || '<text x="0" y="'||(p_height+9)
                     ||'" font-family="Arial" font-size="9"'
                     ||' fill="black" >B</text>'
;
    else 
      l_svg := l_svg || '<text x="0" y="'||(p_height-2)
                     ||'" font-family="Arial" font-size="9"'
                     ||' fill="black" >-'
||p_percent_span||'%</text>';
      l_svg := l_svg || '<text x="'||(p_width*2-18)||'" y="'||(p_height-2)
                     ||'" font-family="Arial" font-size="9" fill="black"'
                     ||' >'
||p_percent_span||'%</text>';
    end if;


    l_svg := l_svg || '</svg>';


    return l_svg;

end;

Thats it for the function. We may head back to our interactive report containing the EMP table. There, we will now add an additional column:

select e.*
, DRAW_INDICATOR_SVG(
    e.EMPNO
  , (e.SAL / 2500) - 1
  , (-e.DEPTNO / 20) + 1
  ) INDICATOR
from EMP e
  • e.EMPNO is our ID for the indicator
  • (e.SAL / 2500) - 1 will scale the value for our top indicator between -1 and 1
  • (-e.DEPTNO / 20) + 1 scales the department number of the employee to a value between -1 and 1

The very last step is to set the column to "Standard Report Column".

Done. The Result will probably look like:

You may have a look on the live-exmaple here