posts - 40, comments - 13, trackbacks - 0, articles - 1
  IT博客 :: 首页 :: 新随笔 :: 联系 ::  :: 管理
(转贴)csdn上高手给出的svg例子(画饼图),求职应聘,必备良药
利用XSLT生成SVG格式的饼图

孟宪会  
2002-3-27 13:34:47

--------------------------------------------------------------------------------
 

上次介绍了如何利用XSLT来生成SVG格式的普通柱形图,这次我们介绍如何生成饼图。饼图也是数据统计时常用的表现方式之一。  
首先,我们准备所需要的数据,格式是XML的。由于目前许多大型数据库都提供了对XML查询结果的支持,因此,您可以直接利用数据库来生成XML数据文件,也可以经过查询,再用编写组件的方法来生成需要的XML数据文件。我们的XML数据文件Data.xml格式如下: 
 
<?xml version="1.0" encoding="UTF-16"?> 
<?xml-stylesheet type="text/xsl" href="PieSVGGraph.xsl"?> 
<总销售额> 
<月销售额 月数="1"> 
<产品销售额 产品编号="Mxh001" 销售数量="5" 销售额="1500.00"/> 
<产品销售额 产品编号="Mxh002" 销售数量="10" 销售额="500.00"/> 
<产品销售额 产品编号="Mxh003" 销售数量="1" 销售额="700.00"/> 
<产品销售额 产品编号="Mxh004" 销售数量="2" 销售额="1800.00"/> 
<产品销售额 产品编号="Mxh005" 销售数量="2" 销售额="1000.00"/> 
<产品销售额 产品编号="Mxh006" 销售数量="12" 销售额="1400.00"/> 
</月销售额> 
<月销售额 月数="2"> 
<产品销售额 产品编号="Mxh001" 销售数量="4" 销售额="1400.00"/> 
<产品销售额 产品编号="Mxh002" 销售数量="12" 销售额="2300.00"/> 
<产品销售额 产品编号="Mxh003" 销售数量="2" 销售额="1500.00"/> 
<产品销售额 产品编号="Mxh004" 销售数量="2" 销售额="1800.00"/> 
<产品销售额 产品编号="Mxh005" 销售数量="1" 销售额="1050.00"/> 
<产品销售额 产品编号="Mxh006" 销售数量="8" 销售额="1600.00"/> 
</月销售额> 
<!-- 限于篇幅所限,我们这里省略了3月份到12月份的数据,如果浏览时,请自行添加上,格式同前 --> 
</总销售额> 
 
下面,我们看看最关键的XSLT部分,由于在这里用到了XSLT,为了能够更好地看到本例的效果,请安装最新的 MsXML3.0+SP2或MsXML4.0,可以到微软的网站下载:http://www.microsoft.com/xml/。文中每一代码段都进行了注释说明。PieSVGGraph.xsl: 
 
<?xml version="1.0" encoding="UTF-16"?> 
<xsl:stylesheet version="1.0" exclude-result-prefixes="xsl Sin Cos" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:Sin="http://lucky.myrice.com/Sin" 
xmlns:Cos="http://lucky.myrice.com/Cos"> 
 
<xsl:output method="xml" indent="yes" media-type="image/svg+xml"/> 
<!-- 先定义三角函数计算模板 --> 
<!-- 
========================================================================== 
计算sin(x)、cos(x)值的模板,用来返回三角函数sin(x)、cos(x)的值 
 
参数说明: 
$X - 输入的角度值,单位是弧度或角度,由下面的Unit参数确定。 
$Unit - [可选参数] 角度 $X 的单位,可选择的值如下: 
'角度' 代表$X的单位是角度; 
'弧度' 代表$X的单位是弧度(默认值)。 
$EPS - [可选参数] 计算结果的精度。增加小数位数可以更大的精确度,但会降低性能。 
========================================================================== --> 
<xsl:template name="sin"> 
<xsl:param name="X"/> 
<xsl:param name="Unit" select="'弧度'"/> 
<xsl:param name="EPS" select="0.00000001"/> 
<xsl:call-template name="FUNC_Cal"> 
<xsl:with-param name="Fun" select="$DOC/Sin:*[1]"/> 
<xsl:with-param name="X" select="$X"/> 
<xsl:with-param name="Unit" select="$Unit"/> 
<xsl:with-param name="EPS" select="$EPS"/> 
</xsl:call-template> 
</xsl:template> 
 
<xsl:template name="cos"> 
<xsl:param name="X"/> 
<xsl:param name="Unit" select="'弧度'"/> 
<xsl:param name="EPS" select="0.00000001"/> 
<xsl:call-template name="FUNC_Cal"> 
<xsl:with-param name="Fun" select="$DOC/Cos:*[1]"/> 
<xsl:with-param name="X" select="$X"/> 
<xsl:with-param name="Unit" select="$Unit"/> 
<xsl:with-param name="EPS" select="$EPS"/> 
</xsl:call-template> 
</xsl:template> 
 
 
<!-- 定义常量pi,代替π --> 
<xsl:variable name="pi" select="3.1415926535897932384626433832795"/> 
 
<!-- 下面我们利用sin(x)和cos(x)的展开式来近似计算他们的值。 --> 
<!-- 采用循环的办法是计算,循环结束的条件是前后两次的结果差值小于我们给定的精度。 --> 
<Sin:Sin/> 
<Cos:Cos/> 
<xsl:variable name="DOC" select="document('')/*"/> 
<xsl:template name="FUNC_Cal"> 
<xsl:param name="Fun" select="/.."/> 
<xsl:param name="X"/> 
<xsl:param name="Unit" select="'弧度'"/> 
<xsl:param name="EPS" select="0.00000001"/> 
<!-- 把角度转换成弧度 --> 
<xsl:variable name="Rads" select="(($Unit = '弧度') * $X) + ((not($Unit = '弧度')) * ($X * $pi div 180))"/> 
<!-- 应用相应的函数 --> 
<xsl:apply-templates select="$Fun"> 
<xsl:with-param name="X" select="$Rads"/> 
<xsl:with-param name="EPS" select="$EPS"/> 
</xsl:apply-templates> 
</xsl:template> 
 
<xsl:template name="Func_sin" match="Sin:*"> 
<xsl:param name="X"/> 
<xsl:param name="EPS" select="0.00000001"/> 
<xsl:variable name="Y"> 
<xsl:choose> 
<xsl:when test="not(0 <= $X and $pi*2 > $X)"> 
<xsl:call-template name="COSIN"> 
<xsl:with-param name="LEN" select="$pi*2"/> 
<xsl:with-param name="X" select="$X"/> 
</xsl:call-template> 
</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="$X"/> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:variable> 
<xsl:call-template name="SINE"> 
<xsl:with-param name="X2" select="$Y*$Y"/> 
<xsl:with-param name="Result" select="$Y"/> 
<xsl:with-param name="Element" select="$Y"/> 
<xsl:with-param name="N" select="1"/> 
<xsl:with-param name="EPS" select="$EPS"/> 
</xsl:call-template> 
</xsl:template> 
 
<xsl:template name="SINE"> 
<xsl:param name="X2"/> 
<xsl:param name="Result"/> 
<xsl:param name="Element"/> 
<xsl:param name="N"/> 
<xsl:param name="EPS"/> 
<xsl:variable name="varN" select="$N+2"/> 
<xsl:variable name="varNewElement" select="-$Element*$X2 div ($varN*($varN - 1))"/> 
<xsl:variable name="varNewResult" select="$Result + $varNewElement"/> 
<xsl:variable name="varDiffResult" select="$varNewResult - $Result"/> 
<xsl:choose> 
<xsl:when test="$varDiffResult > $EPS or $varDiffResult < -$EPS"> 
<xsl:call-template name="SINE"> 
<xsl:with-param name="X2" select="$X2"/> 
<xsl:with-param name="Result" select="$varNewResult"/> 
<xsl:with-param name="Element" select="$varNewElement"/> 
<xsl:with-param name="N" select="$varN"/> 
<xsl:with-param name="EPS" select="$EPS"/> 
</xsl:call-template> 
</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="$varNewResult"/> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:template> 
 
<xsl:template name="Func_cos" match="Cos:*"> 
<xsl:param name="X"/> 
<xsl:param name="EPS" select="0.00000001"/> 
<xsl:call-template name="Func_sin"> 
<xsl:with-param name="X" select="$pi div 2 - $X"/> 
<xsl:with-param name="EPS" select="$EPS"/> 
</xsl:call-template> 
</xsl:template> 
 
<xsl:template name="COSIN"> 
<xsl:param name="LEN"/> 
<xsl:param name="X"/> 
<xsl:variable name="SignX"> 
<xsl:choose> 
<xsl:when test="$X >= 0">1</xsl:when> 
<xsl:otherwise>-1</xsl:otherwise> 
</xsl:choose> 
</xsl:variable> 
<xsl:variable name="varDiff" select="$LEN*floor($X div $LEN) -$X"/> 
<xsl:choose> 
<xsl:when test="$varDiff*$X > 0"> 
<xsl:value-of select="$SignX*$varDiff"/> 
</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="-$SignX*$varDiff"/> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:template> 
 
<!-- 定义饼图的大小 --> 
<xsl:param name="pie_RX" select="number(200)"/> 
<xsl:param name="pie_RY" select="$pie_RX"/> 
<xsl:param name="pie_explode_RX" select="number(20)"/> 
<xsl:param name="pie_explode_RY" select="$pie_explode_RX"/> 
<!-- 确定饼图的中心坐标点 --> 
<xsl:param name="pie_CX" select="$pie_RX + 150"/> 
<xsl:param name="pie_CY" select="$pie_RY + 50"/> 
<xsl:param name="pie_segment_explode" select="number(0)"/> 
<!-- 确定饼图中第一块圆弧的角度 --> 
<xsl:param name="pie_start_angle" select="number(0)"/> 
<!-- 定义最小显示的百分比数 --> 
<xsl:param name="Lowest_Must_Show" select="number(4)"/> 
<xsl:key name="kProdSales" match="产品销售额" use="@产品编号"/> 
 
<!-- 样式单模板开始定义 --> 
<xsl:template match="/"> 
<!-- 得到所有产品的列表 --> 
<xsl:variable name="ProdSales" select="总销售额/月销售额/产品销售额[generate-id() = generate-id(key('kProdSales',@产品编号))]"/> 
<!-- 得到所有产品的销售总值 --> 
<xsl:variable name="TotalSalesvalue" select="sum(总销售额/月销售额/产品销售额/@销售额)"/> 
<!-- SVG 图象开始 --> 
<svg id="body" viewBox="0 0 -100 {$pie_CY + $pie_RY + 75}"> 
<!-- 递归遍历所有产品 --> 
<xsl:call-template name="draw_segments"> 
<xsl:with-param name="Every_Prod_Sales" select="$ProdSales"/> 
<xsl:with-param name="total_sales" select="$TotalSalesvalue"/> 
</xsl:call-template> 
<!-- 饼图的标题 --> 
<text text-anchor="middle" stroke="#000099" font-size-adjust="+1" x="{$pie_CX - $pie_RX}" y="{$pie_CY + $pie_RY + 50}" dx="{2 * $pie_RX}"> 
X 公司个产品销售情况 
</text> 
</svg> 
</xsl:template> 
 
<!-- 递归模板,画出每一部分 --> 
<xsl:template name="draw_segments"> 
<xsl:param name="Every_Prod_Sales"/> 
<xsl:param name="total_sales"/> 
<!-- 递归调用时的参数 --> 
<xsl:param name="total_perc_so_far" select="number(0)"/> 
<xsl:param name="node_Num" select="number(1)"/> 
<!-- 产品节点数 --> 
<xsl:param name="drawn_Num" select="number(1)"/> 
<!-- 实际画出的数 --> 
<xsl:param name="Other_Perc" select="number(0)"/> 
<xsl:choose> 
<xsl:when test="$Every_Prod_Sales[$node_Num]"> 
<!-- 计算一种产品的总销售额 --> 
<xsl:variable name="this_prod_sales" select="sum(key('kProdSales',$Every_Prod_Sales[$node_Num]/@产品编号)/@销售额)"/> 
<!-- 计算该产品在总销售额中的百分比 --> 
<xsl:variable name="perc_of_total" select="$this_prod_sales div $total_sales"/> 
<!-- 实际画出的产品数:如果百分比小于设定的最小百分比,就忽略掉,放到其它一项中 --> 
<xsl:choose> 
<xsl:when test="$perc_of_total < ($Lowest_Must_Show div 100)"> 
<xsl:call-template name="draw_segments"> 
<xsl:with-param name="Every_Prod_Sales" select="$Every_Prod_Sales"/> 
<xsl:with-param name="total_sales" select="$total_sales"/> 
<xsl:with-param name="total_perc_so_far" select="$total_perc_so_far"/> 
<xsl:with-param name="node_Num" select="$node_Num + 1"/> 
<xsl:with-param name="drawn_Num" select="$drawn_Num"/> 
<xsl:with-param name="Other_Perc" select="$Other_Perc + $perc_of_total"/> 
</xsl:call-template> 
</xsl:when> 
<xsl:otherwise> 
<xsl:call-template name="draw_pie_part"> 
<xsl:with-param name="angle" select="360 * $perc_of_total"/> 
<xsl:with-param name="prev_angle" select="$pie_start_angle + (360 * $total_perc_so_far)"/> 
<xsl:with-param name="label" select="concat($Every_Prod_Sales[$node_Num]/@产品编号,' (',format-number($perc_of_total * 100,'0.0'),'%)')"/> 
<xsl:with-param name="explode" select="($pie_segment_explode = $drawn_Num) or ($pie_segment_explode = -1)"/> 
<xsl:with-param name="fill_color"> 
<xsl:call-template name="Pie_Color"> 
<xsl:with-param name="Rel_Pos" select="$drawn_Num"/> 
</xsl:call-template> 
</xsl:with-param> 
<xsl:with-param name="RX" select="$pie_RX"/> 
<xsl:with-param name="radiusY" select="$pie_RY"/> 
<xsl:with-param name="centerX" select="$pie_CX"/> 
<xsl:with-param name="centerY" select="$pie_CY"/> 
<xsl:with-param name="explode_RX" select="$pie_explode_RX"/> 
<xsl:with-param name="explode_radiusY" select="$pie_explode_RY"/> 
</xsl:call-template> 
<!-- 统计下一个节点 --> 
<xsl:call-template name="draw_segments"> 
<xsl:with-param name="Every_Prod_Sales" select="$Every_Prod_Sales"/> 
<xsl:with-param name="total_sales" select="$total_sales"/> 
<xsl:with-param name="total_perc_so_far" select="$total_perc_so_far + $perc_of_total"/> 
<xsl:with-param name="node_Num" select="$node_Num + 1"/> 
<xsl:with-param name="drawn_Num" select="$drawn_Num + 1"/> 
<xsl:with-param name="Other_Perc" select="$Other_Perc"/> 
</xsl:call-template> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:when> 
<xsl:when test="$Other_Perc > 0"> 
<xsl:call-template name="draw_pie_part"> 
<xsl:with-param name="angle" select="360 * $Other_Perc"/> 
<xsl:with-param name="prev_angle" select="$pie_start_angle + (360 * $total_perc_so_far)"/> 
<xsl:with-param name="label" select="concat('其它 (',format-number($Other_Perc * 100,'0.0'),'%)')"/> 
<xsl:with-param name="explode" select="($pie_segment_explode < 0)"/> 
<xsl:with-param name="fill_color"> 
<xsl:call-template name="Pie_Color"> 
<xsl:with-param name="Rel_Pos" select="$drawn_Num"/> 
</xsl:call-template> 
</xsl:with-param> 
<xsl:with-param name="RX" select="$pie_RX"/> 
<xsl:with-param name="radiusY" select="$pie_RY"/> 
<xsl:with-param name="centerX" select="$pie_CX"/> 
<xsl:with-param name="centerY" select="$pie_CY"/> 
<xsl:with-param name="explode_RX" select="$pie_explode_RX"/> 
<xsl:with-param name="explode_radiusY" select="$pie_explode_RY"/> 
</xsl:call-template> 
</xsl:when> 
</xsl:choose> 
</xsl:template> 
 
<!-- 画出每一块单独的弧 --> 
<xsl:template name="draw_pie_part"> 
<xsl:param name="angle"/> 
<xsl:param name="prev_angle"/> 
<xsl:param name="label"/> 
<xsl:param name="explode" select="false()"/> 
<xsl:param name="fill_color" select="'#000099'"/> 
<xsl:param name="line_color" select="'#000000'"/> 
<xsl:param name="RX" select="number(200)"/> 
<xsl:param name="radiusY" select="number(200)"/> 
<xsl:param name="centerX" select="$RX + 50"/> 
<xsl:param name="centerY" select="$radiusY + 50"/> 
<xsl:param name="explode_RX" select="number(20)"/> 
<xsl:param name="explode_radiusY" select="number(20)"/> 
<!-- 计算一种产品占的角度 --> 
<xsl:variable name="full_angle" select="$prev_angle + $angle"/> 
<!-- 计算图例文字所在的角度位置 --> 
<xsl:variable name="half_angle" select="$prev_angle + ($angle div 2)"/> 
<xsl:variable name="X0"> 
<xsl:choose> 
<xsl:when test="$explode"> 
<xsl:variable name="sinX"> 
<xsl:call-template name="sin"> 
<xsl:with-param name="X" select="$half_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="$centerX + ($sinX * $explode_RX)"/> 
</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="$centerX"/> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:variable> 
<xsl:variable name="Y0"> 
<xsl:choose> 
<xsl:when test="$explode"> 
<xsl:variable name="cosX"> 
<xsl:call-template name="cos"> 
<xsl:with-param name="X" select="$half_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="$centerY + (0 - ($cosX * $explode_radiusY))"/> 
</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="$centerY"/> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:variable> 
<!-- 计算坐标点 --> 
<xsl:variable name="X1"> 
<xsl:variable name="sinX"> 
<xsl:call-template name="sin"> 
<xsl:with-param name="X" select="$prev_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="($sinX * $RX)"/> 
</xsl:variable> 
<xsl:variable name="Y1"> 
<xsl:variable name="cosX"> 
<xsl:call-template name="cos"> 
<xsl:with-param name="X" select="$prev_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="0 - ($cosX * $radiusY)"/> 
</xsl:variable> 
<xsl:variable name="X2"> 
<xsl:variable name="sinX"> 
<xsl:call-template name="sin"> 
<xsl:with-param name="X" select="$full_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="($sinX * $RX) - $X1"/> 
</xsl:variable> 
<xsl:variable name="Y2"> 
<xsl:variable name="cosX"> 
<xsl:call-template name="cos"> 
<xsl:with-param name="X" select="$full_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="(0 - ($cosX * $radiusY)) - $Y1"/> 
</xsl:variable> 
<!-- 计算图例出现的象限位置,决定图例文字出现的位置 --> 
<xsl:variable name="label_quandrant" select="floor(($half_angle mod 360) div 90)"/> 
<xsl:variable name="textXalign"> 
<xsl:choose> 
<xsl:when test="$label_quandrant < 2"> 
<xsl:text>start</xsl:text> 
</xsl:when> 
<xsl:otherwise> 
<xsl:text>end</xsl:text> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:variable> 
<xsl:variable name="textDY"> 
<xsl:choose> 
<xsl:when test="($label_quandrant = 0) or ($label_quandrant = 3)"> 
<xsl:text>-10</xsl:text> 
</xsl:when> 
<xsl:otherwise> 
<xsl:text>15</xsl:text> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:variable> 
<!-- 计算图例文字的x、y坐标 --> 
<xsl:variable name="XTxt"> 
<xsl:variable name="sinX"> 
<xsl:call-template name="sin"> 
<xsl:with-param name="X" select="$half_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="$X0 + ($sinX * ($RX + 10))"/> 
</xsl:variable> 
<xsl:variable name="YTxt"> 
<xsl:variable name="cosX"> 
<xsl:call-template name="cos"> 
<xsl:with-param name="X" select="$half_angle"/> 
<xsl:with-param name="Unit" select="'角度'"/> 
</xsl:call-template> 
</xsl:variable> 
<xsl:value-of select="$Y0 + (0 - ($cosX * $radiusY))"/> 
</xsl:variable> 
<!-- 画出每个产品所占的部分 --> 
<g> 
<!-- 画出图 --> 
<path fill="{$fill_color}" stroke="{$line_color}" d="M{$X0},{$Y0} l{$X1},{$Y1} a{$RX},{$radiusY} 0 0 1 {$X2},{$Y2} L{$X0},{$Y0}"/> 
<!-- 画出旁边的图例文字 --> 
<text text-anchor="{$textXalign}" x="{$XTxt}" y="{$YTxt}" dy="{$textDY}"> 
<xsl:value-of select="$label"/> 
</text> 
</g> 
</xsl:template> 
 
<!-- 计算饼图每部分的颜色 --> 
<xsl:template name="Pie_Color"> 
<xsl:param name="Rel_Pos" select="number(0)"/> 
<xsl:variable name="ColorLight" select="'F0E0A09070503010E0C0A08060402000'"/> 
<xsl:text>#</xsl:text> 
<xsl:choose> 
<xsl:when test="($Rel_Pos mod 3) = 0">EE</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="substring($ColorLight,(($Rel_Pos mod 16) * 2)+1,2)"/> 
</xsl:otherwise> 
</xsl:choose> 
<xsl:choose> 
<xsl:when test="($Rel_Pos mod 3) = 1">EE</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="substring($ColorLight,(($Rel_Pos mod 16) * 2)+1,2)"/> 
</xsl:otherwise> 
</xsl:choose> 
<xsl:choose> 
<xsl:when test="($Rel_Pos mod 3) = 2">EE</xsl:when> 
<xsl:otherwise> 
<xsl:value-of select="substring($ColorLight,(($Rel_Pos mod 16) * 2)+1,2)"/> 
</xsl:otherwise> 
</xsl:choose> 
</xsl:template> 
 
</xsl:stylesheet> 
 
我们最后生成的SVG图象如下图所示。 
 
 


此主题相关图片如下:

Feedback

# ttjsainc  回复  更多评论   

2008-04-07 20:19 by ttjsainc
<a href="http://abvrhoya.com">oxgtbkjf</a> kewdxraa http://srvmaejo.com jxbabldi wiaxzsyg [URL=http://vwlwbzfm.com]rkogcicc[/URL]
只有注册用户登录后才能发表评论。