来博客园很久了,也从大牛们那里学到了好多东西。今天开始写博客,写下自己的技术点滴!
前两天接到公司一个需求,是公司手机网站那边要求提供一个接口,用于生成报表图片,方便手机用户查看。公司的报表原先好多是基于FlashChat的,但是用flash是不能满足需求的,好多手机是不支持flash的,而且即使支持也会比较消耗流量。所以需要把报表画到图片上,这样就可以直接调用了。
由于对GDI+也不是太熟悉,花了一天时间才搞定,期间查了很多资料。最后还是被我搞出来了,哈哈,鉴于怕以后有其它类似需求,我做了下封装。现提供给大家,有需要的朋友可以拿走。目前封装的还不是太完美,缺点是纵轴目前只支持6个刻度。纵轴刻度的生成算法是,取数据最小值,最大值然后差值平均为5份。
效果如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace ImageTest
{
/// <summary>
/// MakeImage 的摘要说明
/// </summary>
public class MakeImage
{
private List<string> xStr = new List<string>();
/// <summary>
/// x轴的刻度
/// </summary>
public List<string> XStr
{
set { this.xStr = value; }
get { return this.xStr; }
}
private List<string> yStr = new List<string>();
/// <summary>
/// y轴的刻度
/// </summary>
public List<string> YStr
{
set { this.yStr = value; }
get { return this.yStr; }
}
/// <summary>
/// 计算后的数据点在图片中的坐标
/// </summary>
private List<Point> pointItem = new List<Point>();
/// <summary>
/// 数据点集合
/// </summary>
public List<float> PointDataEsf;
/// <summary>
/// 生成图片
/// </summary>
/// <returns>字节数组</returns>
public byte[] BuildTrendPic()
{
int width = 550;
int height = 300;
if (PointDataEsf.Count < 6)
{
return new byte[0];
}
Bitmap bmp = new Bitmap(width, height);
Graphics gph = Graphics.FromImage(bmp);
gph.InterpolationMode = InterpolationMode.HighQualityBicubic;
gph.SmoothingMode = SmoothingMode.HighQuality;
gph.Clear(Color.White);
gph.DrawRectangle(new Pen(Color.Silver), 0, 0, bmp.Width - 1, bmp.Height - 1);
Point zero = new Point(50, 29);
gph.DrawLine(Pens.Black, zero, new Point(zero.X, 250));
gph.DrawLine(Pens.Black, new Point(zero.X, 250), new Point(530, 250));
//计算Y轴间隔和Y轴数据 最大,最小千位取整 平均为5份
List<int> avgSpan = this.ySpanAvg(PointDataEsf);
for (int i = avgSpan[0]; i < (avgSpan[1]); i += avgSpan[2])
{
this.YStr.Add(i.ToString());
}
this.YStr.Add((avgSpan[1]).ToString());
//计算x轴刻度间隔
int xSpanPix = Convert.ToInt32(480.0 / (this.XStr.Count));
//计算每个数据点在坐标图中的像素位置
for (int i = 0; i < PointDataEsf.Count; i++)
{
Point p = new Point();
double tts = ((PointDataEsf[i] - avgSpan[0]) * 221.0) / (avgSpan[1] - avgSpan[0]);
p.Y = 250 - Convert.ToInt32(tts);
p.X = 80 + i * xSpanPix;
this.pointItem.Add(p);
}
int xspan = 22;
//画横线
for (int i = 1; i <= 10; i++)
{
gph.DrawLine(Pens.Silver, new Point(zero.X, 250 - i * xspan), new Point(530, 250 - i * xspan));
if (i % 2 == 0)
{
gph.DrawLine(Pens.Black, new Point(zero.X, 250 - i * xspan), new Point(55, 250 - i * xspan));
}
}
//画纵轴文字内容
for (int i = 0; i < 6; i++)
{
gph.DrawString(yStr[i] + "元", new Font("宋体", 10), Brushes.Black, new PointF(0, 250 - i * xspan * 2 - 5));
}
//画横轴和纵线
for (int i = 0; i < xStr.Count; i++)
{
gph.DrawString(xStr[i], new Font("宋体", 10), Brushes.Black, new PointF(zero.X + 20 + i * xSpanPix, 255));
}
for (int i = 0; i < 12; i++)
{
//画大竖线
gph.DrawLine(Pens.Silver, new Point(zero.X + 30 + i * xSpanPix, zero.Y), new Point(zero.X + 30 + i * xSpanPix, 250));
//画小竖线
gph.DrawLine(Pens.Black, new Point(zero.X + 30 + i * xSpanPix, 250), new Point(zero.X + 30 + i * xSpanPix, 255));
}
gph.DrawLine(Pens.Silver, new Point(530, zero.Y), new Point(530, 250));
#region 画折线图
//画折线
gph.DrawLines(new Pen(new SolidBrush(Color.FromArgb(255, 0, 0)), 2f), pointItem.ToArray());
//画折线中的点
int j = 0;
foreach (Point p in pointItem)
{
gph.FillPie(new SolidBrush(Color.Red), p.X - 4, p.Y - 4, 8, 8, 0, 360);
gph.DrawString(PointDataEsf[j].ToString(), new Font("宋体", 10), Brushes.OrangeRed, new PointF(p.X - 12, p.Y - 18));
j++;
}
#endregion
gph.DrawString("本次评估数据截止时间" + DateTime.Now.AddMonths(-1).ToString("yyyy年MM月") + " 更多数据请登录 www.xxxxxx.com", new Font("宋体", 10), Brushes.Black, new PointF(100, 278));
gph.Dispose();
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.Jpeg);
bmp.Dispose();
return ms.ToArray();
}
}
#region 求数组中的最大数 初以5后的数 用于y轴间隔
/// <summary>
/// 求数组中的最大数 除以5后的数 用于y轴间隔
/// </summary>
/// <param name="tempArray"></param>
/// <returns>最大值,最小值,平均值</returns>
private List<int> ySpanAvg(List<float> tempArray)
{
List<int> rStruct = new List<int>();
float lower = tempArray[0];
float higher = tempArray[1];
foreach (float x in tempArray)
{
if (higher < x)
higher = x;
if (lower > x)
lower = x;
}
int hstr = Convert.ToInt32(higher);
int lstr = Convert.ToInt32(lower);
int avgSpan = Convert.ToInt32((higher - lower) / 5.0);
rStruct.Add(Convert.ToInt32(lower));
rStruct.Add(Convert.ToInt32(higher));
rStruct.Add(avgSpan);
return rStruct;
}
#endregion
}
}
用法,在aspx页面添加如下代码
protected void Page_Load(object sender, EventArgs e)
{
List<float> PointItems = new List<float>();
PointItems.Add(537); PointItems.Add(479);
PointItems.Add(836); PointItems.Add(895);
PointItems.Add(611); PointItems.Add(995);
PointItems.Add(998);
MakeImage ImageMake = new MakeImage();
ImageMake.XStr.Add("08月"); ImageMake.XStr.Add("09月");
ImageMake.XStr.Add("10月"); ImageMake.XStr.Add("11月");
ImageMake.XStr.Add("12月"); ImageMake.XStr.Add("01月");
ImageMake.XStr.Add("02月");
ImageMake.PointDataEsf = PointItems;
Response.ClearContent();
Response.ContentType = "image/Jpeg";
byte[] ImageByte = ImageMake.BuildTrendPic();
Response.BinaryWrite(ImageByte);
}
:
目前还不太完善,以后再好好完善下!