svg渲染
这是我在英国Hursley UK测试和开发期间遇到的确切问题。 我已经编写了可以很好地在SVG中绘制条形图,折线图和散点图的代码。 但是我在每次测试中处理的数据都在几个数量级上变化。 在每张图上使用相同的比例是没有用的,因为许多图太小或太大,并且所有值要么在一个角上缩小,要么在页面上绘制。
我需要的是一种自动确定用于每组数据的最佳规模的方法。
注意:要查看本文中的SVG文件,您需要一个SVG查看器,您可以在参考资料中找到它(以及包含所有相关文件的.zip文件)。
一点高中数学
着眼于这个问题,我发现自动缩放图形的最佳方法是查看需要绘图的数据集,确定数据集中包含的最大值,然后使用该值作为其他所有值的比例值将被绘制。
通过一个有效的示例可以更好地说明我的技术。 假设您有一个生成三个值的测试:A,B和C。每次测试运行之后,您都需要将这些值绘制到条形图上,但是每次测试运行时,值的范围都会发生巨大变化。
从第一次测试运行开始,生成的值为:
- A = 100
- B = 50
- C = 25
因此,对于此测试运行,A的读数最高,为100。第一个(也是最重要的)任务是计算图形的比例因子 。 这就是绘制每个值时值得多少个高度的像素。 通过将图形轴的高度除以数据中的最大值来获得比例因子。 因此,当我通过本例研究数学时,这一点将变得更加清楚。
- 为了清楚起见,Y轴始终设置为1000像素高。 这只是使理解数学变得更加容易。
- 数据集中的最大值是100(对于A)。
- 对于此图。 比例因子将是: 1000/100 = 10 。
- 因此,对于此条形图,每个值的高度都值得10像素。
- 要获取每个条形的高度,请将比例因子乘以数据集值。 这意味着每个条的高度将为:
图1.使用比例因子,最大值是100(对于A)
现在,假设在下一次测试运行中生成了以下值:
- A = 2000
- B = 5000
- C = 800
请记住,Y轴仍为1000像素高。 这次的最高值为B列,值为5000。因此缩放比例为1000/5000 = 0.2 。
所以这一次每个条的高度将是:
图2.使用比例因子,最大值是5000(对于B)
如您所见,由于这些条的高度是相对于最大值计算的,因此这些条的高度不会超过1000个像素,并且最终不会绘制得太高。 正是这个缩放因子是SVG动态缩放的关键。 请记住,它是根据最大值计算的; 所有其他值将相应缩放。
既然我已经介绍了比例因子的关键概念,那么我将向您展示如何使用它。 以下示例实现是一个小型Java语言程序,该程序在命令行上采用一组值并以上述示例中所述的方式绘制SVG条形图。 尽管是用Java代码编写的,但重要的是数学。 如有必要,您只需看一下Java代码就可以用您希望的任何语言重写它。
除了只在本文中重现整个代码之外,我仅介绍要点。 我建议您下载示例(请参阅参考资料 ),并在具有行编号功能的编辑器中打开SVG_barchart.java
文件。
首先,在第38-48行中确定传入值的最大值。
最重要的行是第51行,其中计算了Y轴比例因子:
清单1.计算Y轴比例因子
51. YAxisScalingFactor = 1000/(double)largestNumber;
在第58和59行上创建了SVG输出文件。使用SVGout.write(-SVG-)
将SVG写入该文件。 请注意,必须使用各种转义字符,例如\"
作为双引号。这可能使SVG有时难以阅读,但是这是必需的,因为否则Java编译器会错误地解释它,并且代码将无法编译。另外,请注意每条SVG文本行均以\n
开头-这使得在使用SVG查看器的“查看源代码”功能时,更易于阅读SVG。
第70-79行画出X和Y轴。 X轴是一条从[x = 0,y = 1000]到[x = 1000,y = 1000]的简单线,Y轴从[x = 0,y = 0]到[x = 0 ,y = 1000]。 这提供了一组简单的笛卡尔坐标轴。
线82和83在Y轴的顶部和中点画一条虚线。 这只是使图形更易于阅读。
在第86和第87行上,Y轴标记在顶部,而在中途标记。 请注意,您正在使用之前计算出的最大值来标记Y轴。
清单2.标记Y轴
86. SVGout.write("\n <text style=\"fill:black; stroke:none\"
x=\"-10\" y=\"0\" >" + largestNumber + "</text>");
87. SVGout.write("\n <text style=\"fill:black; stroke:none\"
x=\"-10\" y=\"500\" >" + (largestNumber/2) + "</text>");
请注意,这些值是动态的,因为它使用largestNumber
值来标记轴。 如有必要,可以通过除以其他值来轻松添加额外的准则和标签。
条的实际绘制从第90行开始:
清单3.绘制条
90.// The graph is ready to be rendered with the values.
91.for(i=0;i<barChartValues.length;i++){
92.
93. // Calculate the Y position. First work out how high the bar
94. // will be by multiplying the value by the scaling factor.
94. // calculated earlier
95. double barHeight =
96. Integer.parseInt(barChartValues[i]) * YAxisScalingFactor;
97.
98. System.out.println("Bar Height is =" + barHeight);
99.
100. // You now have the height that the bar will be. Need to work
101. // out now where to place the bar. With Y values running
102. // positively down, and the Y-axis being 1000 pixels tall,
103. // simply subtract the bar height from 1000 to get the position
104. // of where to place the bar.
105.
106. double YStart = 1000 - barHeight;
107.
108. // Each of the bars is 100 pixels wide. So to space them out
109. //(with a 10-pixel gap between them), multiply the readings position
110. // in the array by 110.
111.
112. double XPosition = (i*110);
113.
114. // Generate some random numbers for your bar colours
115. int randomRed = random.nextInt(255);
116. int randomGreen = random.nextInt(255);
117. int randomBlue = random.nextInt(255);
118.
119. // You now have all your values ready. Draw the rectangle.
120. SVGout.write("\n<rect x=\""+XPosition+"\" y=\""+
121. YStart+"\" width =\""+ 100 +"\" height=\""+barHeight+
122. "\" style=\"fill:rgb("+randomRed+"
123. ,"+randomGreen+","+randomBlue+");\" /> ");
124.
125.}
尝试自己下载并运行该绘图仪。 传递跨不同数量级的各种值,并查看图形如何随值变化和缩放。 图3显示了一些示例输出。
图3.示例动态生成的SVG条形图
折线图
到目前为止,我已经向您展示了如何动态缩放条形图。 但是,这些并不是唯一的图形类型。 某些形式的数据,尤其是诸如股价或地震数据之类的基于时间的数据,最好在线图中绘制。 您也可以使用类似于条形图的方法缩放线形图。
假设您正在监视数据库中的行数。 每30秒读取一次行数,并将其记录在数组中。 在测试运行结束时,您将获得一个包含10个值的数组:
10,20,30,50,90,25,45,60,70,10
再一次,您需要为每次测试运行计算比例因子。 但是,您不希望自己受到限制,以便仅在具有精确读数的情况下才能绘制图表。 这次,您将不得不在X轴和Y轴上缩放。
绘制折线图的最佳方法是使用称为折线的SVG元素。 折线将成对的值作为X和Y点,并在这些点之间绘制一条连接线。 例如:
<polyline points="0 0, 10 10, 20 20">
在[X = 0,Y = 0],[X = 10,Y = 10]和[X = 20,Y = 20]处绘制3点的线。 这些是您需要根据数据集进行缩放和计算的点。
和以前一样,提供了一个可运行的Java示例程序,该程序可以根据提供的数据缩放和渲染折线图(请参阅参考资料 )。
同样,我将只介绍代码的要点。
首先,计算X轴比例因子。 这取决于您渲染的读数数量,因此需要将X轴上的像素数量除以读数数量。 例如,如果您获取了50个读数,则X轴比例因子将为1000/50 =20。因此,每个读数均沿X轴以20像素的间隔绘制。 当读数作为数组传递时,您只需要将1000除以数组中的元素数即可。
清单4.计算X轴缩放因子
40. XAxisScalingFactor = 1000/(double)valuesToPlot.length;
第41-49行找到传入数组中的最大值。
第54-100行准备好输出文件并绘制X和Y轴,以便可以进行绘制。
Y轴比例因子是在104行上以与条形图示例中完全相同的方式计算的(请参见清单2 )。
第108到125行是渲染该行的位置:
清单5.绘制折线
108. // Render the line
109. SVGout.write("\n<polyline points=\"0 1000,");
110.
111. for(i=0;i<valuesToPlot.length;i++){
112.
113. // Calculate the X position by determining which
114. //value in the array you are dealing with.
115. XValue = ((i+1)*XAxisScalingFactor);
116.
117. YValue = Integer.parseInt(valuesToPlot[i]);
118. YValue = YValue*YAxisScalingFactor;
119. YValue = 1000-YValue;
120.
121. // You now have your polyline point.
122. SVGout.write(" " + XValue + " " + YValue +",\n");
123.
124. }
125. // Close off the polyline.
126. SVGout.write("\" style=\"stroke:red; stroke-width: 3; fill :
127. none;\"/>");
在线109中,启动折线元素,并在轴的原点绘制一个初始点。
接下来,通过数组中的值开始工作。 首先,计算X值。 这是元素在数组中的位置乘以X轴缩放因子。 例如,如果您的数组中有50个元素,则X轴比例因子将为:
-
XAxisScalingFactor
=1000/(double)valuesToPlot.length;
-
XAxisScalingFactor
=1000/(double)50;
-
XAxisScalingFactor
=20
因此,每个值以20像素的间隔绘制,因此遍历数组:
- 第一点:x = 20,<第一值>
- 第二点:x = 40,<第二值>
- 第三点:x = 60,<第三值>
- ...
- ...
- 49点:x = 980,<49值>
- 第50点:x = 1000,<第50个值>
请注意,您使用(i+1)
是因为在数组中,第一个元素的计数器值为零; 如要从第一个元素开始,将添加1
。
接下来,计算Y值:
- 第117行从数组中提取值并将其转换为整数。
- 线118将其乘以Y轴比例因子以确定其位置。
- 最后,从1000中减去它。之所以这样做,是因为Y值在页面的正下方运行。 X轴以1000像素的Y高度水平绘制,因此,要确定此计算的Y位置在X轴上方的高度,请从1000中减去它。
现在您已经有了计算出的X和Y位置,因此可以将此点添加到多段线内联中。
处理完数组中的所有值后,将关闭折线并将样式应用于该线。
尝试运行示例,并以不同的数量级传递各种数字列表。 再次,您将看到图形如何根据传入的最大值进行缩放。此外,请尝试传入不同数量的值,例如10甚至1000。X轴将根据值的数量进行缩放传入。图4显示了一些示例输出。
图4.示例动态生成的SVG线图
散点图
这里涵盖的图形的最终类型是散点图。 这些图非常适合在两个维度上变化的数据。 在这里,您将使用与前面的示例完全相同的方法,尽管这次您有一组要绘制的X和Y轴值。
在示例中,数据以(X-value),(Y-value)
的形式传递。 例如,[1,1],[3,5],[50,2]和[10,34]。
我再次提供了一个Java示例程序,您可以下载该程序(请参阅参考资料 )。 这是代码。
第31-57行确定X值和Y值的最大值。
60-61行计算X和Y轴的比例因子。
67-119行画出了轴。 请注意,这一次您将在图形上绘制四个虚线指导线,并相应地对其进行标记。
第124-157行绘制数据:
清单6.计算并绘制散点图点
122. // The axis and the guide lines are ready; now draw the data.
123. SVGout.write("\n <g style=\"fill:none;
124. stroke:red; stroke-width:3\">");
125.
126. for(i=0;i<dataToPlot.length;i++){
127.
128. // Get the value out of the array.
129. String value = dataToPlot[i];
130.
131. // The data is in the form (X-Value),(Y-Value), so find
132. // the comma and get the values on either side of it.
133. index = value.indexOf (',');
134. String X_Pos = value.substring(0,index);
135. String Y_Pos = value.substring(index+1);
136.
137. // Change them to numbers
138. XValue = Integer.parseInt(X_Pos);
139. YValue = Integer.parseInt(Y_Pos);
140.
141. // Calculate the point's position by using the scaling
142. // factor calculated earlier
143. XValue = XValue*XAxisScalingFactor;
144. YValue = YValue*YAxisScalingFactor;
145. YValue = 1000-YValue;
146.
147. // You now have your point. As it's a scatter plot, it
148. // would look nice with an X, so use the point to draw
149 // a line from the top left to the bottom right, and from
150. // the top right to the bottom left.
151.
152. SVGout.write("\n <line x1=\""+ (int)(XValue-5) +
153 "\" y1=\""+(int)(YValue+5)+ "\" x2=\""+(int)(XValue+5)+
154. "\" y2=\""+(int)(YValue-5)+"\" />");
155. SVGout.write("\n <line x1=\""+ (int)(XValue+5) +
156. "\" y1=\""+(int)(YValue+5)+ "\" x2=\""+(int)(XValue-5)+
157. "\" y2=\""+(int)(YValue-5)+"\" />");
158.}
首先,为该组定义样式(第123行)。
然后,开始处理数据。 在第129行中,您将获得该值。
接下来,通过查找逗号分隔符(第133-135行)将X和Y值分离出来,并将这些值转换为数字(第138-139行)。
然后计算该点的位置(线143-145)。
现在可以绘制该点了。 这次不只是绘制单个点,而是尝试用X标记该点。这是绘制两条线的一种简单情况,一条线从左侧开始5个像素,向上方5个像素,从右侧开始5个像素,向下5个像素。 ,另一个从右5个像素开始,向上5个像素,向左5个像素,向下5个像素结束。 这将导致以计算点为中心的X。
下载并运行示例中包含的散点图绘图仪。
图5.示例动态生成的散点图
进一步改进
为简单起见,并使代码尽可能易读,在此处显示的示例中,我使用了最少的Java代码并生成了SVG。 当然,您可以采用这些示例,并通过添加其他颜色,效果和信息来进一步改进它们。 样本中包括两个条形图绘图仪,其中一个绘制了如前所述的简单矩形(参见图3 )。 另一种稍微复杂些,并使用渐变和阴影,但是计算钢筋高度的方法保持不变。 图6显示了一个示例。
图6.改进的条形图
您可以在图7中看到我自己的一个折线图的示例。 此图显示了我在其上进行测试的数据库中的消息数,这些消息数是随时间变化的。 我添加了徽标,阴影效果和测试运行的统计信息表。
图7.改进的SVG折线图
图8显示了我的一个散点图的示例。 在此测试运行期间,我想比较从数据库删除消息的性能时间。 一组数据基于使用JDBC进行操作,另一组数据基于使用准备好的语句。 同样,它具有徽标,阴影和统计信息表。
图8.改进的SVG散点图
这些图的完整SVG版本均包含在样本中。
摘要
本文通过示例演示了如何根据数据动态缩放SVG图。 通过这些技术以及示例代码,您现在可以呈现自己的图形,并根据自己的需求进行调整。
翻译自: https://www.ibm.com/developerworks/xml/library/x-svggrph/index.html
svg渲染