Click here to Skip to main content
Click here to Skip to main content

Create and display funnel charts on a web page

, 7 Oct 2008
Rate this:
Please Sign up or sign in to vote.
How to create and display funnel charts on a web page with C# and ASP.NET.

Introduction

Sometimes, it is required in to display data (financial) in funnel charts for better user experience, and it might be the case that the developer will have to code the funnel display from scratch. The following article helps in writing the code for funnel charts and display them on a web page.

Using the code

While creating the funnel chart, we divide it into different slices. Each slice represents a different stage. In the example below, every slice is displayed in a different color. Go through the code given below. The namespaces System.Drawing and System.Drawing.Imaging are used in the code to draw the funnel slices and to group them together to form a funnel.

FunnelChartCode

// This structure holds the details of each of the slices 
public struct Slice
{
    public string stageName;
    public int value;
    public double dollars;
    public Point[] coordinates;
    public Color color;
}

private Color[] colorPalette = new Color[]
                            {Color.LightSkyBlue,Color.LightGreen,
                             Color.PaleVioletRed,Color.SteelBlue};
// Following constants are used to initialize
// the funnel maximum height, width, slice gap 
private const int FUNNEL_HEIGHT = 106;
private const int IMAGE_WIDTH = 291;
private const int IMAGE_HEIGHT = 160;
private const int SLICE_GAP = 1; 
private Slice[] funnelSlices;


/// <span class="code-SummaryComment"><summary></span>
/// This method initializes the funnel
/// <span class="code-SummaryComment"></summary></span>
public void InitFunnel(int count)
{
    try
    {
        maxSlice = count;
        funnelSlices = new Slice[count];

        chartBMP = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, 
                              PixelFormat.Format32bppArgb);
        graphicsObj = Graphics.FromImage(chartBMP);
        // Draw the background
        graphicsObj.FillRectangle(new SolidBrush(Color.White), 
                                  0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
   
    }
    catch (Exception ex)
    {
        throw new Exception("Error initialising funnel", ex);
    }
}
//    0--------------3
//     \            /
//      \          /
//      1\_______/2

/// <span class="code-SummaryComment"><summary></span>
/// Calculates the coordinates of the slice
/// <span class="code-SummaryComment"></summary></span>
private void GetCoordinates(int sliceIndex)
{
    int sliceHeight;

    // Calculate the top points ie, 0 & 3
    if (sliceIndex == 0)
    {
        // The top 2 points are same as the funnel
        funnelSlices[sliceIndex].coordinates[0] = FUNNEL_BOUNDS[0];
        funnelSlices[sliceIndex].coordinates[3] = FUNNEL_BOUNDS[3];
    }
    else
    {
        // The top 2 points are same
        // as the bottom 2 points of the previous slice
        funnelSlices[sliceIndex].coordinates[0] = 
          funnelSlices[sliceIndex - 1].coordinates[1];
        funnelSlices[sliceIndex].coordinates[3] = 
          funnelSlices[sliceIndex - 1].coordinates[2];
    }


    // Calculate the bottom 2 points ie, 1 & 2
    if (sliceIndex == funnelSlices.Length - 1)
    {
        // The bottom 2 points are same as the funnel
        funnelSlices[sliceIndex].coordinates[1] = FUNNEL_BOUNDS[1];
        funnelSlices[sliceIndex].coordinates[2] = FUNNEL_BOUNDS[2];
    }
    else
    {

        sliceHeight = GetHeight(sliceIndex);
        // Calculate the other 2 points using the 2 point equation
        // Use Point 0 & 1 of the funnel to calculate point 1 of the slice
        funnelSlices[sliceIndex].coordinates[1].Y = 
          funnelSlices[sliceIndex].coordinates[0].Y + sliceHeight;
        funnelSlices[sliceIndex].coordinates[1].X =
            GetX(
            FUNNEL_BOUNDS[0].X, FUNNEL_BOUNDS[0].Y,
            FUNNEL_BOUNDS[1].X, FUNNEL_BOUNDS[1].Y,
            funnelSlices[sliceIndex].coordinates[1].Y);

        // Use Point 2 & 3 of the funnel to calculate point 2 of the slice
        funnelSlices[sliceIndex].coordinates[2].Y = 
          funnelSlices[sliceIndex].coordinates[0].Y + sliceHeight;
        funnelSlices[sliceIndex].coordinates[2].X =
            GetX(
            FUNNEL_BOUNDS[2].X, FUNNEL_BOUNDS[2].Y,
            FUNNEL_BOUNDS[3].X, FUNNEL_BOUNDS[3].Y,
            funnelSlices[sliceIndex].coordinates[2].Y);

    }
}


/// <span class="code-SummaryComment"><summary></span>
/// This method returns the x coordinate on the line defined by
/// (x1, y1) and (x2, y2) for a given y. 
/// <span class="code-SummaryComment"></summary></span>
private int GetX(int x1, int y1, int x2, int y2, int y)
{
    return (x2 - x1) * (y - y1) / (y2 - y1) + x1;
}


/// <span class="code-SummaryComment"><summary></span>
/// Adds required slice with the name, value, associated revenue and color
/// <span class="code-SummaryComment"></summary></span>
public void AddSlice(string name, int value, double dollars, Color sliceClr)
{
    // Throw exception if it exceeds the max
    // Create a new slice object and set the values
    Slice newSlice = new Slice();
    newSlice.coordinates = new Point[4];
    newSlice.stageName = name;
    newSlice.value = value;
    newSlice.dollars = dollars;
    newSlice.color = sliceClr;

    // Place it in the Slice[] array at appropriate location
    funnelSlices[curSlice++] = newSlice;

    // Update the total value
    totalVal += value;
}
/// <span class="code-SummaryComment"><summary></span>
/// This method adds the gaps between the slices
/// <span class="code-SummaryComment"></summary></span>
public void AddGaps(int i)
{
    funnelSlices[i].coordinates[0].Y = 
      funnelSlices[i].coordinates[0].Y + i * SLICE_GAP;
    funnelSlices[i].coordinates[1].Y = 
      funnelSlices[i].coordinates[1].Y + i * SLICE_GAP;
    funnelSlices[i].coordinates[2].Y = 
      funnelSlices[i].coordinates[2].Y + i * SLICE_GAP;
    funnelSlices[i].coordinates[3].Y = 
      funnelSlices[i].coordinates[3].Y + i * SLICE_GAP;

}


/// <span class="code-SummaryComment"><summary></span>
/// This method plots the graph.
/// To be called after all the values have been initialized
/// <span class="code-SummaryComment"></summary></span>
public void PlotGraph()
{
    int i;
    SizeF strSz;
    string label;

    labelTop = FUNNEL_BOUNDS[0].Y;
    // Throw exception if all slices not initialised
    // Repeat the following steps for each of the slices
    for (i = 0; i < maxSlice; i++)
    {
        // Get the coordinates of the slice
        GetCoordinates(i);
    }

    // Get the height of the font
    int labelHeight = (int)graphicsObj.MeasureString("B",labelFont).Height;
    for (i = 0; i < maxSlice; i++)
    {
        // Add gaps between slices
        AddGaps(i);

        // Calculate label top (and bottom for next loop)
        labelTop = (funnelSlices[i].coordinates[0].Y > labelBottom + 1 ? 
                    funnelSlices[i].coordinates[0].Y : labelBottom + 1);
        labelBottom = labelTop + labelHeight;

        // Plot the graph
        graphicsObj.FillPolygon(new SolidBrush(funnelSlices[i].color), 
                                funnelSlices[i].coordinates);
        
        // Add the stage name label on the left
        strSz = graphicsObj.MeasureString(funnelSlices[i].stageName, labelFont);
        graphicsObj.DrawString(funnelSlices[i].stageName, labelFont, 
                               fontBrush, funnelSlices[i].coordinates[0].X - 
                               strSz.Width, labelTop);
        
        

        // Add the value label on the right
        label = ((funnelSlices[i].dollars)).ToString("C0") + "(" + 
                  funnelSlices[i].value.ToString() + ")";
        graphicsObj.DrawString(label, labelFont, fontBrush, 
                    funnelSlices[i].coordinates[3].X,labelTop);

        SummationValue += float.Parse(funnelSlices[i].dollars.ToString());
        TotalDealsOpps += int.Parse(funnelSlices[i].value.ToString());
        
    }
    label = (SummationValue).ToString("C0") + "(" + 
             TotalDealsOpps.ToString() + ")";



    strSz = graphicsObj.MeasureString(label, labelFont);

    int funnelWidth = (FUNNEL_BOUNDS[3].X - FUNNEL_BOUNDS[0].X);
    int labelX = FUNNEL_BOUNDS[0].X + (int)(funnelWidth / 2 - strSz.Width / 2);
    int labelY = (int)(IMAGE_HEIGHT - strSz.Height );

    
    graphicsObj.DrawString(label, labelFont, fontBrush, labelX, labelY);
    // Saves the created funnel as an image in Response.OutStream 
    Response.ContentType = IMAGE_FORMAT;
    chartBMP.Save(Response.OutputStream, ImageFormat.Jpeg);
}

All the above code should be placed in an ASPX page code-behind. The page load event of this page will load the data to be displayed. Please go through the code given above and also the attached files. To display the funnel in a web page, the following code can be used:

<div class="blockBody">
   <table cellpadding="0" cellspacing="0" border="0">
       <tr>
           <td align="center">
               <img src="FunnelChart.aspx" /> 
               <!--<span class="code-comment"> FunnelChart.aspx is the page where the above 
                    code is placed as code behind --></span>
           </td>
       </tr>
    </table>
</div>

Points of interest

I went through many already available funnel chart code, but found coding and displaying it in a web page is much more fun and easy. This work was much appreciated by my clients when we went live.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Vani Kulkarni
Software Developer (Senior)
India India
Software developer(Senior) working on .Net Applications.
Domains worked on:
-Finance
-Insurance
-Digital Asset Management
 
Applications include both web and windows.
 
Completed Computer Science & Engineering from B.V.B.College of Engineering and Technology, Hubli.

Comments and Discussions

 
QuestionNice article. PinmemberMalli_S12-Sep-12 1:38 
AnswerRe: Nice article. PinmemberVani Kulkarni1-Jan-13 18:40 
GeneralMy vote of 4 PinmemberMalli_S12-Sep-12 1:38 
QuestionMy 4 PinmemberMalli_S12-Sep-12 1:36 
Questionfunnel chart in mvc3 application? Pinmemberchandru910-Aug-12 21:25 
GeneralMy vote of 5 PinmemberPankaj Nikam19-Jun-12 2:14 
GeneralRe: My vote of 5 PinmemberVani Kulkarni25-Jun-12 19:02 
GeneralMy vote of 1 PinmemberwhizMos7-Sep-10 0:57 
QuestionNo other way? PinmemberGravityGuy6-Apr-09 13:14 
GeneralGood effert but... Pinmembernakul6513-Oct-08 21:07 
GeneralVery Nice PinmemberAbhijit Jana7-Oct-08 20:26 
GeneralRe: Very Nice PinmemberVani Kulkarni8-Oct-08 2:55 
GeneralVery Interesting PinmemberSam T Mathew7-Oct-08 19:10 
GeneralRe: Very Interesting PinmemberVani Kulkarni7-Oct-08 20:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 7 Oct 2008
Article Copyright 2008 by Vani Kulkarni
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid