2005.08.31 06:11 PM

Sparklines 2.3 (for Matt)

Just a quick question. It isn't anything too hard to do, but how about a little helper function that would take the d parameter and automatically find the high/low point and then scale the other points relative to that? maybe add a new parameter called "doscale" and run the function if set to "true"

i.e. Have a data stream of "1,5,6,3,4,6,7,6,3,5,6,7,8" - normally, this produces a VERY straight line with little variation. the function would pick 1 as low and 8 as high. The 8 would then become 100 and the 1 a 0. Then, each other point would be scaled accordingly.

After the release of version 2.2, Matt asked again:

Hey there :). remember me from version 1? lol Just wondering if you had a chance to think about the data normalization. i.e. converting (1,4,5,3,1,4,9) to (0,40,60,20,0,40,100)

That kind of perseverance deserves something, so this one's for Matt.

The new data scaling functionality is invoked using a scale=true parameter. Note that the smooth sparkline's range-lower and range-upper parameters, and the discrete sparkline's upper parameter, are still expected to reflect values in the 0-100 range. Also, for bars, a decision had to be made about what to do with the 0-bar(s), which inevitably arise with scaling that treats the smallest value(s) as 0. Ultimately, rather than eliminate them or show them as zero-length bars, I decided to give them a value of 1.

The new version is backward-compatible so I simply replaced the source.

Here are some IMG elements pointing at the updated spark.ashx handler showing the same sets of data unscaled and scaled, along with their querystrings.

 sparkline querystring type=smooth&d=0,10,1,9,2,8,3,7,4,6,5&height=25& step=3&last-m=false&max-m=true&min-m=true type=smooth&d=0,10,1,9,2,8,3,7,4,6,5&height=25& step=3&last-m=false&max-m=true&min-m=true& scale=true type=discrete&d=0,4,2,6,5,9,11,10,11,9,5,6,2,4,0&height=15 type=discrete&d=0,4,2,6,5,9,11,10,11,9,5,6,2,4,0&height=15& scale=true type=bars&d=1,10,8,3&width=50 type=bars&d=1,10,8,3&width=50& scale=true

Let me know if you have any questions.

ok, now THAT is what I call service :)

thanks!

Matt | 2005.09.01 08:34 AM

No problem, Matt. Hope you can use it.

ewbi.develops | 2005.09.01 08:48 AM

I'd love to include this idea in an ASP.NET book (with attribution). Email me!

Scott Hanselman | 2005.10.03 09:59 PM

Eric it's great... would it be possible to have an option to draw the bar type sparks vertically instead of horizontally?

That way it would be possible to embed them in a sentence, at the same height as the text. Now they end up being pretty "high" if you have 12 bars (one for each period in a year for example.)

tim | 2006.12.10 04:01 AM

Hi Tim,

I've been meaning to do that every since I put this thing together. Clearly the vertical ones make more sense than the horizontal, as they are really just an alternative styling of the existing discrete style.

I added the horizontal barlines originally to support a particular project I was doing, and I kind of regret it, as the scaling was weird for 0 value case, and anyway I switched to divs in the project that led me to add them in the first place.

The problem with adding them now, though, is that I'm booked to the wall. Perhaps over the holidays I'll find some time. I'll send you an email if/when I do.

ewbi.develops | 2006.12.10 08:48 AM

Are there any licensing requirements for using this code in a commercial product?

Earl | 2007.01.23 07:07 AM

Hi Earl, nope.

ewbi.develops | 2007.01.23 07:38 AM

I've implemented column (vertical bar) charts. If you want to give me an email address or some way to send it, I'll be happy to send it your way.

Earl | 2007.01.23 12:26 PM

Hi Earl,

Thanks for the offer. I appreciate it. Turns out I've actually done it, too, along with a better scaling option and a bug fix on ASP.NET 2.0, but I just haven't had time for anything but work for the last few months, so it continues to languish in a folder on my harddrive. Perhaps in February I'll take some time and catch up with all this.

Thanks again for the offer.

ewbi.develops | 2007.01.23 12:52 PM

Very nice work! Impressive documentation and well organzied code...

Any word on getting vertical bars? I'm going to start implementing this myself, but don't want to repeat work if you are going to release your implementation.

DJ | 2007.04.18 09:41 PM

Hi DJ,

I'm afraid it'll be weeks, not days, before I can get to this. My project load is just too heavy right now. If you get a vertical bar implementation together first, though, and you decide to share it publicly, please come back and leave us a link - I know others are interested in this as well.

Good luck.

ewbi.develops | 2007.04.18 11:27 PM

Hi,
May be if you can help me with this.
I am creating a report using Microsoft SSRS, where i need sparkline graphs and performance graphs in a Table/Matrix.
Is it possible to use your sparkline component for reporting?

Thank you,
Sumit

Sumit | 2007.06.03 08:37 AM

Hi Sumit,

Good question. I don't know. My guess is: not in it's current form. However, the logic could be repackaged in component form and made to work. I'm pretty busy these days, but I'll try to give it a look later this week.

ewbi.develops | 2007.06.03 08:42 AM

thank you!! that wil be of gr8 help..

Sumit | 2007.06.03 08:50 AM

Well, Sumit, what do you know, it works.

If you've got a data source column that contains a comma-delimited data series (or can construct one, perhaps with a view), then you can easily add a calculated column to your report (i.e., an expression) that combines the data series with a fully qualified URL and then use this expression as the source for an image control. Here's the expression I used (I've wrapped it to make it more readable):

="http://www.ewbi.com/ewbi.develop/sparklines/spark.ashx?type=smooth&d="
& Fields!values.Value &
"&height=30&step=3&last-m=false&max-m=true&min-m=true"

It combines my comma-delimited data series (in the values field) with the other pieces of the URL to form a complete sparklines service request. I named this calculated field url. I then added an image control from the toolbox, clicked Cancel on the wizard and manually changed its Source property to External and its Value property to this:

=Fields!url.Value

Switched to the Preview tab and it worked like a champ. Hope this helps you. If I get time I'll probably write a post about this, as it's pretty cool.

Good luck!

ewbi.develops | 2007.06.22 09:03 PM

Thanks for ur response.I really appreciate this.
Actually i wanted to write you regarding this method of using Generic Handler in Reports.
I also found this thing promising, since the only concern here is to get the COMMA separated list, which is infact very much feasible if we are working on asp.net reportviewer or infact if we can create a function which will return the datalist in the query.
But I realised this thing become little tricky when we are using function, lot of parameters to satisfy in the report, to return datalist.
So instead of using function I tried using Custom Code.
I found this http://msdn2.microsoft.com/en-us/library/bb395166.aspx link very useful.

Due to little busy schedule, I wouldnot be able to give more details right now. I will write more on this.

thanks
Sumit

Sumit | 2007.06.29 12:30 AM

Simple Rotate Bar Graph Solution:

All,

I'm guessing someone has found this by now but in case not....

The code below is Matt's "PlotSparklineBars". I added two lines of code to allow for bar graph rotation based on a new querystring parameter.

"// !! NEW"

Good Luck...

TD

MemoryStream PlotSparklineBars(int[] results) {

try {
int barHeight = int.Parse(GetArg("bar-height", "3"));
int height = (barHeight+1)*results.Length+(results.Length-1);
int width = int.Parse(GetArg("width", "20")) + 1;
ArrayList barColors = new ArrayList(GetArg("bar-colors", "blue").Split(new char[] {','}));
bool alignRight = bool.Parse(GetArg("align-right", "false"));
bool transparent = bool.Parse(GetArg("transparent", "false"));
bool scale = bool.Parse(GetArg("scale", "false"));
// !! NEW
bool rotate = bool.Parse(GetArg("rotate", "false"));

ResultsInfo resultsInfo = EvaluateResults(results, scale);

using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb)) {
using (Graphics g = Graphics.FromImage(bitmap)) {

using (SolidBrush br = new SolidBrush(Color.White)) {
g.FillRectangle(br, 0, 0, width, height);
}

using (Pen p = new Pen(shadowColor)) {
for (int y = 0; y 1)
g.DrawLine(p, barLeft+1, barTop+barHeight, barLeft+barWidth, barTop+barHeight);
g.DrawLine(p, barLeft+barWidth, barTop+1, barLeft+barWidth, barTop+barHeight);
}
}

// !! NEW
if(rotate){
bitmap.RotateFlip(System.Drawing.RotateFlipType.Rotate270FlipNone);
}

MemoryStream m = new MemoryStream();
bitmap.Save(m, ImageFormat.Gif);
return (transparent) ? MakeTransparent(m) : m;
}
}
} catch {
return PlotError();
}
}

TD | 2007.07.12 07:31 AM

TD,

Thanks for enhancement. I can't wait to see what your rotation does to the sparkline. I've recently migrated the sparkline.ashx service (and everything else) to an ASP.NET 2.0 hoster, so I've got a number of mods planned to take advantage of it, including adding Joe Gregorio's new impulse style.

ewbi.develops | 2007.07.12 08:06 AM

Matt,

The impulse will be cool..

I meant to mention this before... You wrote a very nice bit of code here. I just plugged it in and viola'

Thanks a ton !
TD

TD | 2007.07.12 04:29 PM

Thanks - I'm glad you're finding it of some use.

Btw, I'm Eric, not Matt. Matt was just a fellow who requested the sparkline modification addressed by this post (see the first comment).

Anyhow, please come back and share any other enhancements you make - we do appreciate it!

ewbi.develops | 2007.07.12 06:09 PM

DOH ! Eric, yes, sorry..

Someone else did that you too....

TD | 2007.07.13 04:23 AM

Eric,

I really appreciate your doing this. We are using it in a web app that graphs out from 150 to 300 distinct sparklines. Each sparkline can be a graph from the last 8 to 24 hours of all tracked statistics in an Emergency Room.

Anyway, my question is this. Do you have any thoughts on speed enhancements ? I'm not sure there are any. If there are, I have not been able to find them. It is fast clean code. When you are creating 300 graphics, any cycle might help...

I just though that you may have thought of some in your work on the next version. Currently, we are only using PlotSparklineSmooth.

Thanks, TD

Tod | 2007.08.03 04:54 AM

Hi Tod,

Glad you've found this useful. I haven't thought much about the speed of this thing. It was mostly an exercise in Python conversion and ASP.NET service development. However, I have seen that for large numbers of calls it is not terribly efficient. A large part of this is network latency; making lots of separate calls (like from a browser) for 10s or even 100s of sparklines (and painting them as they appear) results in just terrible visual performance. Use of the logic in a local assembly in a smart client/Windows form app would probably show significantly better performance, particularly if reworked to share/reuse the graphics/brushes/pens/etc. for more than one sparkline at a time. Beyond these issues, though, I'd say there are probably things that can be done to improve the code's base speed, I just haven't had time to look. One thing that could definitely be improved is the transparency logic, which in this (older) version requires the code to pass over each sparkline's individual pixels - all of them - one at a time. Not good.

I've got some free time coming up next week and some new info on using the sparkline service from a SQL Server Reporting Services report - perhaps these things together, along with some notes on performance, will warrant a new post. (Hard to believe it's been nearly two years since I posted anything here about sparklines!)

Good luck.

ewbi.develops | 2007.08.03 07:03 PM