我有一个格式为多个点 (>= 10000) 的列表
x y
例如
1 2
1.0001 2.003
...
有没有Linux程序可以绘制第1和第2、第2和第3、第n和第(n+1)段之间的段,最好是矢量图形?
答案1
正如 Joseph R. 提到的,有古老的经典gnuplot
,加上许多更现代的产品,可以创建各种格式的各种图形,包括基于矢量的和位图的。但此类程序往往用途广泛,需要相当长的时间才能学习如何正确使用它们,当您只想快速绘制简单的折线图时,这可能有点令人畏惧。
我最近一直在自学 SVG 格式,手动创建小型 SVG 文件,还编写 Python 程序来使用 SVG 绘制各种东西,所以当我看到你的问题时,我认为这是进行更多 SVG 编程练习的绝佳机会。 :)
这是一个原始的 Python 程序,它根据问题中给出的格式的数据制作一个简单的折线图。输出为 SVG 格式,打印到 stdout,因此您需要使用重定向将其保存到文件中。输入数据是从命令行上指定的文件名读取的,但如果没有给出文件名,则程序从 stdin 读取数据,因此该程序可以在管道中使用。
输入数据可以包含空行或#
第一个非空白字符的注释行。每行上的 X 和 Y 值必须由至少一个空格字符分隔(制表符也可以),一行上的其他空格将被忽略,因此 X 值之前或 Y 值之后的空格将被忽略。
该程序扫描所有 XY 数据以查找最大值和最小值,这些值用于计算 SVG viewBox,以便绘图正确缩放和居中。
SVGgraph.py
#! /usr/bin/env python
''' Create a simple line graph as an SVG file
Written by PM 2Ring 2014.11.09
'''
import sys
def bounding_box(points):
xtup, ytup = zip(*points)
xlo = min(xtup)
xhi = max(xtup)
ylo = min(ytup)
yhi = max(ytup)
return xlo, ylo, xhi - xlo, yhi - ylo
def points_to_SVG(points, width, height):
#Get graph bounds & adjust to allow for a margin
xlo, ylo, xsize, ysize = bounding_box(points)
margin = 0.02
xmargin = xsize * margin
ymargin = ysize * margin
xlo -= xmargin
xsize += 2 * xmargin
ylo -= ymargin
ysize += 2 * ymargin
strokewidth = 2.0 * min(xsize, ysize) / float(max(width, height))
head = '''<?xml version="1.0"?>\n<svg xmlns="http://www.w3.org/2000/svg"
width="%d" height="%d" viewBox="%f %f %f %f"
preserveAspectRatio="xMidYMid meet">\n\n''' % (width, height, xlo, ylo, xsize, ysize)
body = ' <polyline points="\n' + '\n'.join(["%f, %f" % t for t in points]) + '\n"\n'
tail = 'style="fill:none; stroke-width:%f; stroke:#006600;"/>\n</svg>' % strokewidth
return head + body + tail
def main():
iname = sys.argv[1] if len(sys.argv) > 1 else None
f = open(iname, 'rt') if iname else sys.stdin
data = f.read().splitlines()
if iname is not None:
f.close()
points = []
for line in data:
#Skip blank lines
if not line: continue
x, y = line.split()
#Skip comments: lines which have '#' as the first non-blank char
if x.startswith('#'): continue
points.append((float(x), float(y)))
width, height = 800, 600
print points_to_SVG(points, width, height)
if __name__ == '__main__':
main()
这是一些示例输出:
图测试.svg
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
width="800" height="600" viewBox="-0.240855 -3.881333 12.524483 7.762666"
preserveAspectRatio="xMidYMid meet">
<polyline points="
0.000000, 0.000000
0.523599, 3.732051
1.047198, 2.598076
1.570796, -0.500000
2.094395, -0.866025
2.617994, 0.267949
3.141593, 0.000000
3.665191, -0.267949
4.188790, 0.866025
4.712389, 0.500000
5.235988, -2.598076
5.759587, -3.732051
6.283185, -0.000000
6.806784, 3.732051
7.330383, 2.598076
7.853982, -0.500000
8.377580, -0.866025
8.901179, 0.267949
9.424778, 0.000000
9.948377, -0.267949
10.471976, 0.866025
10.995574, 0.500000
11.519173, -2.598076
12.042772, -3.732051
"
style="fill:none; stroke-width:0.019407; stroke:#006600;"/>
</svg>
FWIW,这是我用来生成该 SVG 测试数据的程序。
SVGgraph-points.py
#! /usr/bin/env python
''' Create a list of points to test SVGgraph.py with
Written by PM 2Ring 2014.11.09
'''
import sys
from math import pi, sin
def f(x):
return sin(x) + 2.0 * sin(x * 2.0) + 1.5 * sin(x * 3.0)
def make_points(n):
points = n * [None]
for i in xrange(n):
x = 4.0 * pi * i / n
y = f(x)
points[i] = (x, y)
return points
def main():
n = int(sys.argv[1]) if len(sys.argv) > 1 else 24
points = make_points(n)
print '\n'.join(["%f %f" % t for t in points])
if __name__ == '__main__':
main()
用法
python SVGgraph-points.py 24 > testdata
python SVGgraph.py testdata > graphtest.svg
或者
python SVGgraph-points.py | python SVGgraph.py > graphtest.svg
为 SVGgraph-points.py 指定 200 或更高的参数以创建平滑的图形。
正如我上面所说,这只是一个粗略的脚本;我不想添加花哨的命令行处理。 :)
您可能希望在 Python 脚本或 SVG 中修改width
和参数,但它们并不重要,因为 SVG 显示程序通常允许您在查看图像时控制比例。height
即使您在 SVG 文件中编辑这些值,图像也将始终居中并适当缩放,以免任何部分被切断。
您可能还想尝试margin
缩放因子,当前设置为 0.02,它决定了图形周围的最小边距。您可以通过调整乘数来控制绘制线的(标称)粗细strokewidth
,当前设置为 2.0。
玩得开心!
编辑
这是图形脚本的新版本,它使用传统的坐标系,而不是 SVG(和许多其他计算机绘图系统)使用的倒置系统。所以现在你的图表不会颠倒了。 :)
#! /usr/bin/env python
''' Create a simple line graph as an SVG file
Uses a conventional coordinate system,
not the usual inverted SVG system.
Written by PM 2Ring 2014.11.11
'''
import sys
def bounding_box(points):
xtup, ytup = zip(*points)
xlo = min(xtup)
xhi = max(xtup)
ylo = min(ytup)
yhi = max(ytup)
return xlo, ylo, xhi, yhi
def points_to_SVG(points, width, height):
#Get graph bounds & adjust to allow for a margin
xlo, ylo, xhi, yhi = bounding_box(points)
xsize = xhi - xlo
ysize = yhi - ylo
margin = 0.02
xmargin = xsize * margin
ymargin = ysize * margin
xlo -= xmargin
xsize += 2 * xmargin
yhi += ymargin
ysize += 2 * ymargin
strokewidth = 2.0 * min(xsize, ysize) / float(max(width, height))
head = '''<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
width="%d" height="%d" viewBox="%f %f %f %f"
preserveAspectRatio="xMidYMid meet">
<polyline style="fill:none; stroke-width:%f; stroke:#006600;"
transform="scale(1, -1)"
points="\n''' % (width, height, xlo, -yhi, xsize, ysize, strokewidth)
body = '\n'.join(["%f, %f" % t for t in points])
tail = '\n"/>\n</svg>'
return head + body + tail
def main():
iname = sys.argv[1] if len(sys.argv) > 1 else None
f = open(iname, 'rt') if iname else sys.stdin
data = f.read().splitlines()
if iname is not None:
f.close()
points = []
for line in data:
#Skip blank lines
if not line: continue
x, y = line.split()
#Skip comments: lines which have '#' as the first non-blank char
if x.startswith('#'): continue
points.append((float(x), float(y)))
width, height = 800, 600
print points_to_SVG(points, width, height)
if __name__ == '__main__':
main()
答案2
你可以写一个小脚本。像这样的东西:
#!/usr/bin/ruby
require 'rvg/rvg'
require 'scanf'
include Magick
RVG::dpi = 72
rvg = RVG.new(2.5.in, 2.5.in).viewbox(0,0,300,300) do |canvas|
canvas.styles(:stroke=>'black', :stroke_width=>4)
oldx=oldy=nil
ARGF.map{|line| line.scanf("%f %f")}.each do | x,y |
canvas.line(oldx,oldy,x,y) if oldx
oldx,oldy=x,y
end
end
rvg.draw.write('output.png')
这将打开一个新画布,在其上绘制线条并将结果写入文件。