To read the article online, visit http://www.4GuysFromRolla.com/articles/072507-1.aspx

ASP.NET Charting with NPlot

By Olav Lerflaten


Introduction


Three years ago I wrote an article here on 4Guys titled ASP.NET Charting Using Office Web Components (OWC) that examined how to use OWC as a charting engine in ASP.NET applications. OWC is still a very powerful charting engine, but unfortunately it carries with it some serious drawbacks:
  • OWC is a COM library, which needs to be installed on the ASP.NET web server. If you do not run your own web server, you may have difficulty getting these libraries installed.
  • Calling COM libraries from .NET code introduces additional difficulty and overhead that can be avoided by using native .NET DLL's. In contrast to COM components, a .NET library is simply a DLL file that needs only to be distributed with the web application in its /Bin directory. There is no need for a separate installation on the web server.
  • Microsoft's support for OWC has dwindled. Earlier versions of OWC came bundled with Microsoft Office, but this has been discontinued in Office 2007. OWC11, which came bundled with Office 2003, seems to be the final version. There are no signs of a similar .NET library being developed by Microsoft.
There are several commercial charting libraries available for ASP.NET. They are extensively advertised, and can certainly do the job. However, frugal developers like me may prefer freeware alternatives. In this article we will examine using NPlot to create charts in an ASP.NET application. NPlot is a free, .NET native charting engine that excels at scientific charting. Read on to learn more!

Choosing a .NET Charting Engine


When deciding on a charting engine it is important to compare and contrast a number of metrics, such as price, features, support, documentation, and ease of use. As a frugal developer, I strongly prefer freeware options over commercial ones. There are lots of different chart types, and few charting libraries support them all. In particular, there is a distinct difference between business charting - pie and bar charts - and scientific charting - XY or scatter charts. Business-orientated charting libraries usually do a poor job with scientific charting, and vice versa. My primary interest is in scientific charting; more specific, in charting of time series. A time series is a sequence of data points over a time interval. One example is weather data, where you measure the air temperature every minute (or every hour, if you are satisfied with lower resolution). The collection of air temperature data is then known as a time series.

When charting time series, we usually use a XY chart with the X axis as the time axis, and the Y axis as the value axis. The following chart illustrates a time series and shows real weather data. Here, two time series (air pressure and wind speed) are shown within the same chart. This is useful for visual data correlation (sharply falling air pressure usually means increased wind speed). As wind speed and air pressure have different units of measurement, and differ widely in their absolute values of measurement, separate Y axes are used to place the curves close to each other.

A time series chart showing air pressure and wind speed in a time series.

A good listing of available charting controls (both freeware and commercial) can be found at Microsoft's official ASP.NET website under the Control Gallery's Charting Controls section. I have personally tested two freeware charting controls:

WebChart, while adequate in many ways, suffered from poor placement of the time axis labels. Also, the project seems to be abandoned: there have been no updates since 2004 and there is only support for .NET version 1.1.

NPlot scared me away initially because of an almost total lack of documentation and coding examples. However, the few available screenshots looked promising, so I decided to do some research work. This resulted in a number of coding examples using NPlot version 0.9.10.0. These examples are available for download at the end of this article; I believe they may be very useful as a template for other developers. After working through NPlot's quirks, I am happy to say that it is a very good charting engine, which (almost) completely covers my needs. The design focus seems to be scientific charting, although basic business charts (bar charts, line charts, stock charts) seem to be present (although I have not tested business charting extensively). The NPlot library (NPlot.dll) can be downloaded from the NPlot home page. There are different DLLs for the .NET versions 1.1 and 2.0.

NPlot's Programming Philosophy


When generating a chart using NPlot or any other charting engine, the actual chart must be rendered as a temporary image file on the web server. To view this page from a web browser you must use an <img> tag (or an Image Web control) that specifies the URL of this temporary image file. While a chart can be rendered in any image format, I prefer GIF images for charting purposes, as high-contrast images (e.g., black lines on white backgrounds) cause compression artifacts on JPG files.

There are two ways to work with temporary image files on a web server:

  • Save the temporary image as a file in a directory on the web server, and then provide an image link to this file on the web page. This concept is easy to understand, but a bit difficult in practice. The ASP.NET user id on the web server needs write permissions on the directory, you need to create unique file names, and you need logic to erase the temporary files after use, or the directory will soon fill up with old chart images.
  • Alternatively, you can create the temporary image in the web server's memory, and then "stream" the image to the web page as a Memory Stream. This requires a separate ASP.NET page whose purpose is to generate the chart (in memory) and then send the chart image's binary contents down to the web browser. To display this chart in a web page, simply use an <img> element or Image Web control that points to this chart-generating ASP.NET page, like so: <img src="Demo1.aspx" />. Any parameters that need to be supplied can be passed from the main page to the image page using the query string or session variables.
Personally, I prefer the second method. The concept is a bit more difficult, the technique requires an additional ASP.NET page, and any parameters needing to be passed from the main page to the image page requires additional code and logic, but overall, having a separate ASP.NET page is simpler as it unburdens you from the headache of permissions, unique file names, and file system cleanup.

An NPlot Coding Example


While evaluating NPlot I wrote a number of VB.NET demos illustrating how to use NPlot to generate time series. Many of these demos are available for download at the end of this article. Let's examine the first of these demos. Keep in mind that these demos illustrate creating an ASP.NET page whose express purpose is to generate a temporary chart image file in memory and to return this binary data to the client. Therefore, these demos do not contain any markup in the .aspx file. The .aspx portion contains just a reference to the code-behind file and, optionally, an @OutputCache directive to improve performance.

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Demo1.aspx.vb" Inherits="Demo1"%>
<%@ OutputCache Duration="5" VaryByParam="none" %>

The @OutputCache directive specifies the cache lifetime (5 seconds in this example). If the database contains frequently updated info (such as weather data or stock prices), you need to set the cache lifetime low in order to get an updated chart each time this image is viewed. If the underlying data is updated less frequently, you can increase the cache's duration in order to improve performance. For more information on output caching, see Caching with ASP.NET and Output Caching in ASP.NET 2.0.

The code-behind file (Demo1.aspx.vb) contains the code that generates the image file and streams it to the visitor's browser. It's where all of the action takes place!

'Add-in software (please obtain NPlot.dll from http://www.nplot.com/):

'NPlot - A charting library for .NET
'Copyright (C) 2003-2006 Matt Howlett and others.
'All rights reserved.

Imports System
Imports System.Web.UI
Imports System.Drawing
Imports Microsoft.VisualBasic.VBMath

Public Class Demo1
   Inherits System.Web.UI.Page

   Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      '... Put user code to initialize the page here...

      'Define MemoryStream
      Dim memStream As New System.IO.MemoryStream

      'PlotSurface for the Bitmap graph:
      Dim npSurface As New NPlot.Bitmap.PlotSurface2D(975, 625)

      'One Plot for each timeseries:
      Dim npPlot1 As New NPlot.LinePlot
      Dim npPlot2 As New NPlot.LinePlot
      Dim npPlot3 As New NPlot.LinePlot

      'Font definitions:
      Dim TitleFont As New System.Drawing.Font("Arial", 12)
      Dim AxisFont As New System.Drawing.Font("Arial", 10)
      Dim TickFont As New System.Drawing.Font("Arial", 8)

      'Legend definition:
      Dim npLegend As New NPlot.Legend


      'Create the Underlying Data...
      Dim i As Integer
      Dim X1(1440) As DateTime
      Dim X2(1440) As DateTime
      Dim X3(1440) As DateTime
      Dim Y1(1440) As Double
      Dim Y2(1440) As Double
      Dim Y3(1440) As Double

      'Seed to give variable wavelength on Timeseries 2:
      Dim Random1 As Double = Rnd() * 80 + 20

      'Generate 3 sample timeseries for yesterday (one measurement per minute):
      For i = 0 To 1440
         'Generate X values (one per minute):
         X1(i) = DateTime.Now.Date.AddDays(-1).AddMinutes(i)
         X2(i) = DateTime.Now.Date.AddDays(-1).AddMinutes(i)
         X3(i) = DateTime.Now.Date.AddDays(-1).AddMinutes(i)

         'Timeseries 1: Stable signal with some noise:
         Y1(i) = Rnd() * 10 + 30

         'Timeseries 2: Sine wave with some noise:
         Y2(i) = 40 * Math.Sin(i / Random1) + Rnd() * 10 + 50

         'Timeseries 3: Random walk from initial value:
         If i = 0 Then
            Y3(i) = 50
         Else
            Y3(i) = Y3(i - 1) + Rnd() - 0.5
         End If
      Next


      'Prepare PlotSurface:
      npSurface.Clear()
      npSurface.Title = "Demo1"
      npSurface.BackColor = Color.White
      npSurface.TitleFont = TitleFont

      'Left Y axis grid:
      npSurface.Add(New NPlot.Grid, NPlot.PlotSurface2D.XAxisPosition.Bottom, NPlot.PlotSurface2D.YAxisPosition.Left)

      'Timeseries 1 to Plot 1:
      npPlot1.AbscissaData = X1
      npPlot1.DataSource = Y1
      npPlot1.Color = Color.Blue
      npPlot1.Label = "Timeseries 1"

      'Timeseries 2 to Plot 2:
      npPlot2.AbscissaData = X2
      npPlot2.DataSource = Y2
      npPlot2.Color = Color.Purple
      npPlot2.Label = "Timeseries 2"

      'Timeseries 3 to Plot 3:
      npPlot3.AbscissaData = X3
      npPlot3.DataSource = Y3
      npPlot3.Color = Color.Red
      npPlot3.Label = "Timeseries 3"


      'Add each Plot to the PlotSurface. Here you can choose to map each Plot to either:
      '-The left or right Y axis
      '-The bottom or top X axis
      'Plot 1 to bottom X axis, left Y axis:
      npSurface.Add(npPlot1, NPlot.PlotSurface2D.XAxisPosition.Bottom, NPlot.PlotSurface2D.YAxisPosition.Left)

      'Plot 2 to bottom X axis, left Y axis:
      npSurface.Add(npPlot2, NPlot.PlotSurface2D.XAxisPosition.Bottom, NPlot.PlotSurface2D.YAxisPosition.Left)

      'Plot 3 to bottom X axis, left Y axis:
      npSurface.Add(npPlot3, NPlot.PlotSurface2D.XAxisPosition.Bottom, NPlot.PlotSurface2D.YAxisPosition.Left)


      'Prepare bottom X axis (a Plot MUST first be assigned to the bottom X axis):
      npSurface.XAxis1.Label = "Timestamp"
      npSurface.XAxis1.LabelFont = AxisFont
      npSurface.XAxis1.TickTextFont = TickFont
      npSurface.XAxis1.NumberFormat = "yyyy-MM-dd HH:mm"
      npSurface.XAxis1.TicksLabelAngle = 90
      npSurface.XAxis1.TickTextNextToAxis = True
      npSurface.XAxis1.FlipTicksLabel = True
      npSurface.XAxis1.LabelOffset = 110
      npSurface.XAxis1.LabelOffsetAbsolute = True


      'Prepare left Y axis (a Plot MUST first be assigned to the left Y axis):
      npSurface.YAxis1.Label = "Value"
      npSurface.YAxis1.LabelFont = AxisFont
      npSurface.YAxis1.TickTextFont = TickFont
      npSurface.YAxis1.NumberFormat = "{0:####0.0}"


      'Add legend:
      npLegend.AttachTo(NPlot.PlotSurface2D.XAxisPosition.Top, NPlot.PlotSurface2D.YAxisPosition.Right)
      npLegend.VerticalEdgePlacement = NPlot.Legend.Placement.Inside
      npLegend.HorizontalEdgePlacement = NPlot.Legend.Placement.Outside
      npLegend.BorderStyle = NPlot.LegendBase.BorderType.Line
      npSurface.Legend = npLegend


      'Update PlotSurface:
      npSurface.Refresh()


      'Save PlotSurface to MemoryStream, stream output as GIF file:
      Response.Buffer = True
      Response.ContentType = "image/gif"
      npSurface.Bitmap.Save(memStream, System.Drawing.Imaging.ImageFormat.Gif)
      memStream.WriteTo(Response.OutputStream)
      Response.End()

   End Sub

End Class

As you can see, the code-behind class requires a good deal of code. The code, though, is fairly straightforward as it simply generates the underlying data, specifies the plot surface settings, creates the series, plots the points of the series, and streams the binary contents of the chart image file to the browser. The NPlot data model is based on a "Plot Surface", analogous to a blank sheet of paper. To this surface, you can add one or more plots (collections of X and Y values). Each plot can be bound to the left or right Y axis, and the bottom or top X axis. Usually, only one X and Y axis are used; but if you wish to fit two time series with widely different amplitudes and/or different time intervals within the same chart, two independent X and Y axes are useful.

The DataSource property of each plot is the collection of Y values, while the AbscissaData property is the collection of X values. If the AbscissaData property is not specified, the X values are assumed to be 0, 1, 2, 3...; and you get in effect a business type charting, where the X axis is a categorization rather than a measured value.

The most unfamiliar part of the code for most developers is probably the MemoryStream concept (used in the last few lines of the code). As explained earlier, this is done to avoid saving temporary image files on the web server. The entire Demo1.aspx page, in effect, is transformed into an image, which can be referenced by other web pages using the <img> HTML tag or via the ASP.NET Image Web control.

To run this code, download the ZIP file at the end of this article. The ZIP file includes a Visual Studio 2003 project, but you can open it in Visual Studio 2005. If you are using Visual Studio 2002, simply create a new project then manually copy over the files from the download into your project. In addition to the above demo, the ZIP file includes three other demos and the NPlot assembly in its /Bin directory. The screenshot below shows the output of Demo1.aspx when viewed through a browser.

A time series chart with three series.

The time series are generated from a random seed, so they will change each time the page is visited. Therefore, the chart on your screen might not look exactly like the one above.

Chart Types in NPlot


The Demo1 example uses the LinePlot chart type. This is a familiar chart type, with straight lines drawn between each data point. However, for scientific charting, two other chart types might also be considered:
  • StepPlot: Each measurement is assumed to be stable until the next measurement, that is, we do not assume interpolated values between each measurement. Graphically, this is shown as horizontal "steps" between each measurement. One example where this might be useful is if each new measurement reflects a mean value since the last measurement.
  • PointPlot: Each measurement is represented by a dot symbol rather than as a line between two points.
The StepPlot and PointPlot data models are almost identical to the LinePlot data model. For charts like Demo1, where the data points are close together, the StepPlot and PointPlot types are less useful. But for less populated charts they should be considered as an alternative. The following chart illustrates these three separate chart types.

This chart uses LinePlot, StepPlot, and PointPlot series.

Conclusion


If you are looking for a free charting engine for your ASP.NET applications, NPlot might be what you are looking for. NPlot does not cover every conceivable chart type, but for scientific and simple business use, it is certainly worth considering. The documentation is extremely poor, but the coding examples in this article should get you started.

Happy Programming!

  • By Olav Lerflaten


    Attachments


  • Download the NPlot Coding Demo
  • Further Readings


  • ASP.NET Charting Using Office Web Components (OWC)
  • A Look at WebCharts, a Free .NET Charting Control
  • The NPlot Homepage
  • An NPlot Overview (PDF)
  • Powerful Web Charting with NPlot
  • About the Author


    Olav Lerflaten is an IT professional living in Trondheim, Norway. He specializes in industrial data processing.

    Article Information
    Article Title: ASP.NET.ASP.NET Charting with NPlot
    Article Author: Olav Lerflaten
    Published Date: July 25, 2007
    Article URL: http://www.4GuysFromRolla.com/articles/072507-1.aspx


    Copyright 2014 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers