svg_plot_gen 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/Gemfile.lock +14 -0
- data/LICENSE.txt +17 -0
- data/README.md +56 -0
- data/bin/svg_plot_gen +10 -0
- data/example.svg +188 -0
- data/lib/cursor_line.js +9 -0
- data/lib/font.css +6 -0
- data/lib/svg_plot_gen.rb +252 -0
- data/svg_plot_gen.gemspec +16 -0
- metadata +90 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
2
|
+
of this software and associated documentation files (the "Software"), to deal
|
3
|
+
in the Software without restriction, including without limitation the rights
|
4
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
5
|
+
copies of the Software, and to permit persons to whom the Software is
|
6
|
+
furnished to do so, subject to the following conditions:
|
7
|
+
|
8
|
+
The above copyright notice and this permission notice shall be included in
|
9
|
+
all copies or substantial portions of the Software.
|
10
|
+
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
12
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
13
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
14
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
15
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
16
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
17
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
**svg-plot-gen** creates the axes and gridlines for a graph in [SVG](https://developer.mozilla.org/en/docs/SVG). Perfect for creating a static background to which live data can be added.
|
2
|
+
|
3
|
+
## When **not** to use this
|
4
|
+
|
5
|
+
You just want to plot a graph. If you're using Python, check out [Pygal](http://pygal.org/first_steps/), otherwise take a look at [gnuplot](http://www.gnuplot.info/).
|
6
|
+
|
7
|
+
## When to use this
|
8
|
+
|
9
|
+
When you've got the same format of graph that you just want to plot over and over again, and you haven't got an enviroment that supports plotting graphs some other way.
|
10
|
+
Maybe you're working on an embedded system, have a bash script that for some reason can't use [gnuplot](http://www.gnuplot.info/) or you're trying to make a
|
11
|
+
[CouchDB](http://couchdb.apache.org/) [list function](http://wiki.apache.org/couchdb/Formatting_with_Show_and_List) output an SVG graph.
|
12
|
+
|
13
|
+
Another rationale for using this is that you don't want to waste processing power generating axes and gridlines every time you want to output a graph.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
**svg-plot-gen** is written in [ruby](http://www.ruby-lang.org/) and uses [nokogiri](http://nokogiri.org/) to output SVG.
|
18
|
+
|
19
|
+
You'll need to install [ruby](http://www.ruby-lang.org/) and [bundler](http://gembundler.com/) if you haven't already got them installed. Then is should just be a case of running
|
20
|
+
|
21
|
+
```
|
22
|
+
bundle install
|
23
|
+
```
|
24
|
+
|
25
|
+
to get all the required gems.
|
26
|
+
|
27
|
+
## What it does
|
28
|
+
|
29
|
+
It outputs an SVG file that looks somewhat like [this](example.svg) to stdout.
|
30
|
+
|
31
|
+
**That's all.**
|
32
|
+
|
33
|
+
## Usage ##
|
34
|
+
|
35
|
+
Run
|
36
|
+
|
37
|
+
```
|
38
|
+
ruby svg_plot_gen.rb --help
|
39
|
+
```
|
40
|
+
|
41
|
+
to see details of all the command line options.
|
42
|
+
|
43
|
+
## Templates
|
44
|
+
|
45
|
+
Specifing the `-t` option means the output will contain two `[SPLIT]` tags that can be replaced with data by a stream editor like `sed` or whatever string manipulation functions exist
|
46
|
+
in your language.
|
47
|
+
|
48
|
+
The first `[SPLIT]` tag is for a line graph. If a path is substituted in using the format specified [here](https://developer.mozilla.org/docs/SVG/Attribute/d) then this will be
|
49
|
+
overlayed on the graph.
|
50
|
+
|
51
|
+
The second `[SPLIT]` tag is in the [display](https://developer.mozilla.org/docs/SVG/Attribute/display) attribute of a 'No Data Found' marker, and can be substituted for `inline`
|
52
|
+
to display this marker.
|
53
|
+
|
54
|
+
## License ##
|
55
|
+
|
56
|
+
MIT
|
data/bin/svg_plot_gen
ADDED
data/example.svg
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 960 380" width="960" height="380" onmousemove="on_mouse_move(event)" style="font-family: 'Exo'">
|
4
|
+
<defs>
|
5
|
+
<style type="text/css"><![CDATA[@font-face {
|
6
|
+
font-family: 'Exo';
|
7
|
+
font-style: normal;
|
8
|
+
font-weight: 400;
|
9
|
+
src: local('Exo Regular'), local('Exo-Regular'), url(http://themes.googleusercontent.com/static/fonts/exo/v1/wocyXRLWPo2Av-yUTmmbTA.woff) format('woff');
|
10
|
+
}
|
11
|
+
]]></style>
|
12
|
+
</defs>
|
13
|
+
<script type="text/javascript"><![CDATA[function on_mouse_move(event) {
|
14
|
+
if (event.clientX > 100 && event.clientX < 935) {
|
15
|
+
document.getElementById("cursor_line").setAttribute("display","inherit");
|
16
|
+
document.getElementById("cursor_line").setAttribute("transform", "translate(" + event.clientX + ", 0)");
|
17
|
+
} else {
|
18
|
+
document.getElementById("cursor_line").setAttribute("display","none");
|
19
|
+
}
|
20
|
+
}
|
21
|
+
function null_handler(event) {}
|
22
|
+
]]></script>
|
23
|
+
<path id="cursor_line" d="M0,25l0,285" stroke="#cb4b16" stroke-width="1.5" display="none"/>
|
24
|
+
<g style="color:grey" stroke="currentColor" transform="translate(0,25)">
|
25
|
+
<path d="m100.0 0v285"/>
|
26
|
+
<path d="m134.8 0v285"/>
|
27
|
+
<path d="m169.6 0v285"/>
|
28
|
+
<path d="m204.4 0v285"/>
|
29
|
+
<path d="m239.2 0v285"/>
|
30
|
+
<path d="m274.0 0v285"/>
|
31
|
+
<path d="m308.8 0v285"/>
|
32
|
+
<path d="m343.5 0v285"/>
|
33
|
+
<path d="m378.3 0v285"/>
|
34
|
+
<path d="m413.1 0v285"/>
|
35
|
+
<path d="m447.9 0v285"/>
|
36
|
+
<path d="m482.7 0v285"/>
|
37
|
+
<path d="m517.5 0v285"/>
|
38
|
+
<path d="m552.3 0v285"/>
|
39
|
+
<path d="m587.1 0v285"/>
|
40
|
+
<path d="m621.9 0v285"/>
|
41
|
+
<path d="m656.7 0v285"/>
|
42
|
+
<path d="m691.5 0v285"/>
|
43
|
+
<path d="m726.3 0v285"/>
|
44
|
+
<path d="m761.0 0v285"/>
|
45
|
+
<path d="m795.8 0v285"/>
|
46
|
+
<path d="m830.6 0v285"/>
|
47
|
+
<path d="m865.4 0v285"/>
|
48
|
+
<path d="m900.2 0v285"/>
|
49
|
+
<path d="m935.0 0v285"/>
|
50
|
+
</g>
|
51
|
+
<g style="color:black" stroke="currentColor" transform="translate(0,25)">
|
52
|
+
<path d="m100.0 0v10m0 265v10"/>
|
53
|
+
<path d="m134.8 0v5m0 275v5"/>
|
54
|
+
<path d="m169.6 0v10m0 265v10"/>
|
55
|
+
<path d="m204.4 0v5m0 275v5"/>
|
56
|
+
<path d="m239.2 0v10m0 265v10"/>
|
57
|
+
<path d="m274.0 0v5m0 275v5"/>
|
58
|
+
<path d="m308.8 0v10m0 265v10"/>
|
59
|
+
<path d="m343.5 0v5m0 275v5"/>
|
60
|
+
<path d="m378.3 0v10m0 265v10"/>
|
61
|
+
<path d="m413.1 0v5m0 275v5"/>
|
62
|
+
<path d="m447.9 0v10m0 265v10"/>
|
63
|
+
<path d="m482.7 0v5m0 275v5"/>
|
64
|
+
<path d="m517.5 0v10m0 265v10"/>
|
65
|
+
<path d="m552.3 0v5m0 275v5"/>
|
66
|
+
<path d="m587.1 0v10m0 265v10"/>
|
67
|
+
<path d="m621.9 0v5m0 275v5"/>
|
68
|
+
<path d="m656.7 0v10m0 265v10"/>
|
69
|
+
<path d="m691.5 0v5m0 275v5"/>
|
70
|
+
<path d="m726.3 0v10m0 265v10"/>
|
71
|
+
<path d="m761.0 0v5m0 275v5"/>
|
72
|
+
<path d="m795.8 0v10m0 265v10"/>
|
73
|
+
<path d="m830.6 0v5m0 275v5"/>
|
74
|
+
<path d="m865.4 0v10m0 265v10"/>
|
75
|
+
<path d="m900.2 0v5m0 275v5"/>
|
76
|
+
<path d="m935.0 0v10m0 265v10"/>
|
77
|
+
</g>
|
78
|
+
<g style="text-anchor:middle" stroke="none" transform="translate(0,334)" font-size="12pt" fill="#000">
|
79
|
+
<g transform="translate(100.0)">
|
80
|
+
<text>00:00</text>
|
81
|
+
</g>
|
82
|
+
<g transform="translate(169.6)">
|
83
|
+
<text>02:00</text>
|
84
|
+
</g>
|
85
|
+
<g transform="translate(239.2)">
|
86
|
+
<text>04:00</text>
|
87
|
+
</g>
|
88
|
+
<g transform="translate(308.8)">
|
89
|
+
<text>06:00</text>
|
90
|
+
</g>
|
91
|
+
<g transform="translate(378.3)">
|
92
|
+
<text>08:00</text>
|
93
|
+
</g>
|
94
|
+
<g transform="translate(447.9)">
|
95
|
+
<text>10:00</text>
|
96
|
+
</g>
|
97
|
+
<g transform="translate(517.5)">
|
98
|
+
<text>12:00</text>
|
99
|
+
</g>
|
100
|
+
<g transform="translate(587.1)">
|
101
|
+
<text>14:00</text>
|
102
|
+
</g>
|
103
|
+
<g transform="translate(656.7)">
|
104
|
+
<text>16:00</text>
|
105
|
+
</g>
|
106
|
+
<g transform="translate(726.3)">
|
107
|
+
<text>18:00</text>
|
108
|
+
</g>
|
109
|
+
<g transform="translate(795.8)">
|
110
|
+
<text>20:00</text>
|
111
|
+
</g>
|
112
|
+
<g transform="translate(865.4)">
|
113
|
+
<text>22:00</text>
|
114
|
+
</g>
|
115
|
+
<g transform="translate(935.0)">
|
116
|
+
<text>00:00</text>
|
117
|
+
</g>
|
118
|
+
</g>
|
119
|
+
<g style="text-anchor: middle" transform="translate(517 367)" font-size="14pt" fill="#000">
|
120
|
+
<text>x-axis</text>
|
121
|
+
</g>
|
122
|
+
<g style="color:grey" stroke="currentColor" transform="translate(100,0)">
|
123
|
+
<path d="m0 310.0h835"/>
|
124
|
+
<path d="m0 253.0h835"/>
|
125
|
+
<path d="m0 196.0h835"/>
|
126
|
+
<path d="m0 139.0h835"/>
|
127
|
+
<path d="m0 82.0h835"/>
|
128
|
+
<path d="m0 25.0h835"/>
|
129
|
+
</g>
|
130
|
+
<g style="color:black" stroke="currentColor" transform="translate(100,0)">
|
131
|
+
<path d="m0 310.0h10m815 0h10"/>
|
132
|
+
<path d="m0 287.3h5m825 0h5"/>
|
133
|
+
<path d="m0 270.2h5m825 0h5"/>
|
134
|
+
<path d="m0 260.1h5m825 0h5"/>
|
135
|
+
<path d="m0 253.0h10m815 0h10"/>
|
136
|
+
<path d="m0 230.3h5m825 0h5"/>
|
137
|
+
<path d="m0 213.2h5m825 0h5"/>
|
138
|
+
<path d="m0 203.1h5m825 0h5"/>
|
139
|
+
<path d="m0 196.0h10m815 0h10"/>
|
140
|
+
<path d="m0 173.3h5m825 0h5"/>
|
141
|
+
<path d="m0 156.2h5m825 0h5"/>
|
142
|
+
<path d="m0 146.1h5m825 0h5"/>
|
143
|
+
<path d="m0 139.0h10m815 0h10"/>
|
144
|
+
<path d="m0 116.3h5m825 0h5"/>
|
145
|
+
<path d="m0 99.2h5m825 0h5"/>
|
146
|
+
<path d="m0 89.1h5m825 0h5"/>
|
147
|
+
<path d="m0 82.0h10m815 0h10"/>
|
148
|
+
<path d="m0 59.3h5m825 0h5"/>
|
149
|
+
<path d="m0 42.2h5m825 0h5"/>
|
150
|
+
<path d="m0 32.1h5m825 0h5"/>
|
151
|
+
<path d="m0 25.0h10m815 0h10"/>
|
152
|
+
</g>
|
153
|
+
<g style="text-anchor:end" stroke="none" transform="translate(94,0)" font-size="12pt" fill="#000">
|
154
|
+
<g transform="translate(0,316.0)">
|
155
|
+
<text>1000</text>
|
156
|
+
</g>
|
157
|
+
<g transform="translate(0,259.0)">
|
158
|
+
<text>10000</text>
|
159
|
+
</g>
|
160
|
+
<g transform="translate(0,202.0)">
|
161
|
+
<text>100000</text>
|
162
|
+
</g>
|
163
|
+
<g transform="translate(0,145.0)">
|
164
|
+
<text>1e+06</text>
|
165
|
+
</g>
|
166
|
+
<g transform="translate(0,88.0)">
|
167
|
+
<text>1e+07</text>
|
168
|
+
</g>
|
169
|
+
<g transform="translate(0,31.0)">
|
170
|
+
<text>1e+08</text>
|
171
|
+
</g>
|
172
|
+
</g>
|
173
|
+
<g style="text-anchor:middle" transform="translate(25 167) rotate(-90)" font-size="14pt" fill="#000">
|
174
|
+
<text>y-axis</text>
|
175
|
+
</g>
|
176
|
+
<g stroke="#333" fill="none">
|
177
|
+
<path d="m100 25v285h835v-285h-835z"/>
|
178
|
+
</g>
|
179
|
+
<a xlink:title="Plot #1">
|
180
|
+
<g stroke-width="1.4" stroke-linejoin="bevel" fill="none">
|
181
|
+
<path stroke="#dc322f" d=""/>
|
182
|
+
</g>
|
183
|
+
</a>
|
184
|
+
<g style="text-anchor:middle" stroke="#dc322f" transform="translate(517,167)" font-size="30pt" display="none" fill="#dc322f">
|
185
|
+
<text>No Data Found</text>
|
186
|
+
</g>
|
187
|
+
<rect x="0" y="0" width="960" height="380" fill="white" fill-opacity="0" stroke="none" stroke-width="0" onclick="null_handler"/>
|
188
|
+
</svg>
|
data/lib/cursor_line.js
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
function on_mouse_move(event) {
|
2
|
+
if (event.clientX > [XSTART] && event.clientX < [XEND]) {
|
3
|
+
document.getElementById("cursor_line").setAttribute("display","inherit");
|
4
|
+
document.getElementById("cursor_line").setAttribute("transform", "translate(" + event.clientX + ", 0)");
|
5
|
+
} else {
|
6
|
+
document.getElementById("cursor_line").setAttribute("display","none");
|
7
|
+
}
|
8
|
+
}
|
9
|
+
function null_handler(event) {}
|
data/lib/font.css
ADDED
data/lib/svg_plot_gen.rb
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
# Richard Meadows 2013
|
2
|
+
# Generates the SVG for a graph
|
3
|
+
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'color'
|
6
|
+
require 'trollop'
|
7
|
+
|
8
|
+
class Svg_Plot_Gen
|
9
|
+
def self.gen
|
10
|
+
# Command line options
|
11
|
+
opts = Trollop::options do
|
12
|
+
opt :template, "Output a template SVG file that contains [SPLIT] markers where a path and display attribute can be inserted. See README.md", :default => false
|
13
|
+
opt :width, "The width of the output plot in pixels", :default => 960
|
14
|
+
opt :height, "The height of the output plot in pixels", :default => 380
|
15
|
+
opt :x_margin, "The distance that is left between the edge of the image and the x-axis", :default => 70
|
16
|
+
opt :y_margin, "The distance that is left between the edge of the image and the y-axis", :default => 100
|
17
|
+
opt :margins, "The distance that is left between the edge of the image and the plot on sides that do not have labels", :default => 25
|
18
|
+
opt :long_tick_len, "The length of the long ticks on the graph", :default => 10
|
19
|
+
opt :short_tick_len, "The length of the short ticks on the graph", :default => 5
|
20
|
+
opt :font_size, "The size of font used for the plot", :default => 12
|
21
|
+
opt :label_font_size, "The size of font used for the axis labels", :default => 14
|
22
|
+
opt :scale, "The scale that is going to be used on the y axis", :default => 'em'
|
23
|
+
opt :x_text, "The x-axis label", :default => 'x-axis'
|
24
|
+
opt :y_text, "The y-axis label", :default => 'y-axis'
|
25
|
+
end
|
26
|
+
|
27
|
+
axis_padding = 6
|
28
|
+
|
29
|
+
# Define the solarized colours
|
30
|
+
base03 = Color::RGB.from_html('002b36')
|
31
|
+
base02 = Color::RGB.from_html('073642')
|
32
|
+
base01 = Color::RGB.from_html('586e75')
|
33
|
+
base00 = Color::RGB.from_html('657b83')
|
34
|
+
base0 = Color::RGB.from_html('839496')
|
35
|
+
base1 = Color::RGB.from_html('93a1a1')
|
36
|
+
base2 = Color::RGB.from_html('eee8d5')
|
37
|
+
base3 = Color::RGB.from_html('fdf6e3')
|
38
|
+
yellow = Color::RGB.from_html('b58900')
|
39
|
+
orange = Color::RGB.from_html('cb4b16')
|
40
|
+
red = Color::RGB.from_html('dc322f')
|
41
|
+
magenta = Color::RGB.from_html('d33682')
|
42
|
+
violet = Color::RGB.from_html('6c71c4')
|
43
|
+
blue = Color::RGB.from_html('268bd2')
|
44
|
+
cyan = Color::RGB.from_html('2aa198')
|
45
|
+
green = Color::RGB.from_html('859900')
|
46
|
+
|
47
|
+
# Calculate the coordinates for the actual graph itself
|
48
|
+
xstart = opts.y_margin
|
49
|
+
xend = opts.width - opts.margins
|
50
|
+
xlen = xend - xstart
|
51
|
+
ystart = opts.margins
|
52
|
+
yend = opts.height - opts.x_margin
|
53
|
+
ylen = yend - ystart
|
54
|
+
|
55
|
+
# Read in the CSS for our font
|
56
|
+
font_css = File.read(File.dirname(__FILE__) + '/font.css')
|
57
|
+
|
58
|
+
# Load in the javascript for the cursor line script
|
59
|
+
cursor_line_js = File.read(File.dirname(__FILE__) + '/cursor_line.js')
|
60
|
+
# Replace the [XSTART] and [XEND] placeholders in the javascript
|
61
|
+
cursor_line_js = cursor_line_js.gsub(/\[XSTART\]/, xstart.to_s).gsub(/\[XEND\]/, xend.to_s)
|
62
|
+
|
63
|
+
# Set the template marker if specified
|
64
|
+
if opts.template
|
65
|
+
# Put in a split marker when the data actully goes
|
66
|
+
plot = '[SPLIT]'
|
67
|
+
no_data_display = '[SPLIT]'
|
68
|
+
else
|
69
|
+
plot = ''
|
70
|
+
no_data_display = 'none'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Work out the x-positions of our vertical lines and ticks
|
74
|
+
xlines_step = xlen.to_f / 24
|
75
|
+
xlines = Hash[(xstart..xend).step(xlines_step).map { |x| x.round(1) }.zip((0..24).map { |x| (x%2 == 0) })]
|
76
|
+
# Build up our x-axis labels for a 24 hour period
|
77
|
+
xlabels = (0..24).step(2).map { |hour| [['00', (hour%24).to_s].join[-2..-1], ':00'].join }.zip(xlines.select { |k, v| v }.map { |k, v| k })
|
78
|
+
|
79
|
+
# Generates the values for a logarithmic scale
|
80
|
+
if opts.scale == 'em'
|
81
|
+
# Work out the y-positions of the horizonal lines and ticks
|
82
|
+
ylines = Hash[(0..5).map { |y| (y < 5 ? [1, 2.5, 5, 7.5] : [1]).map { |m| [Math.log10((10**y)*m), m==1] } }.flatten(1).map { |y| [(yend - y[0]*(ylen.to_f/5)).round(1), y[1]] }]
|
83
|
+
# Build up our y-axis labels
|
84
|
+
ylabels = ['1000', '10000', '100000', '1e+06', '1e+07', '1e+08'].zip(ylines.select { |k ,v| v }.map { |k, v| k })
|
85
|
+
end
|
86
|
+
# Generate the values for a linear scale
|
87
|
+
if opts.scale == 'battery'
|
88
|
+
# Work out the y-positions of the horizonal lines and ticks
|
89
|
+
ylines = Hash[(0..5).map { |y| (y < 5 ? [0, 0.25, 0.5, 0.75] : [0]).map { |m| [y+m, m==0] } }.flatten(1).map { |y| [(yend - y[0]*(ylen.to_f/5)).round(1), y[1]] }]
|
90
|
+
# Build up our y-axis labels
|
91
|
+
ylabels = (0..5).zip(ylines.select { |k ,v| v }.map { |k, v| k })
|
92
|
+
end
|
93
|
+
|
94
|
+
# Output some useful info about the plot we're generating to stderr
|
95
|
+
$stderr.puts ["X Origin ", xstart].join
|
96
|
+
$stderr.puts ["Y Origin ", yend].join
|
97
|
+
$stderr.puts ["X Length ", xlen].join
|
98
|
+
$stderr.puts ["Y Length ", ylen].join
|
99
|
+
$stderr.puts ["Px per second ", (xlen.to_f/(24*3600)).round(6)].join
|
100
|
+
$stderr.puts ["Px per decade ", (ylen.to_f/5).round(2)].join
|
101
|
+
|
102
|
+
# Create the XML object
|
103
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
104
|
+
xml.doc.create_internal_subset( # Create the DOCTYPE
|
105
|
+
'svg',
|
106
|
+
'-//W3C//DTD SVG 1.1//EN',
|
107
|
+
'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'
|
108
|
+
)
|
109
|
+
xml.svg(:viewbox => [0, 0, opts.width, opts.height].join(' '),
|
110
|
+
:width => opts.width,
|
111
|
+
:height => opts.height,
|
112
|
+
:xmlns => "http://www.w3.org/2000/svg",
|
113
|
+
'xmlns:xlink' => "http://www.w3.org/1999/xlink",
|
114
|
+
:onmousemove => "on_mouse_move(event)",
|
115
|
+
:style => "font-family: 'Exo'") do
|
116
|
+
# Font
|
117
|
+
xml.defs do
|
118
|
+
xml.style(:type => 'text/css') do
|
119
|
+
xml.cdata(font_css)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
# Cursor Line Script
|
123
|
+
xml.script(:type => 'text/javascript') do
|
124
|
+
xml.cdata(cursor_line_js)
|
125
|
+
end
|
126
|
+
# The cursor line itself
|
127
|
+
xml.path(:id => 'cursor_line',
|
128
|
+
:d => ['M0,', ystart, 'l0,', ylen].join,
|
129
|
+
:stroke => orange.html,
|
130
|
+
'stroke-width' => '1.5',
|
131
|
+
:display => 'none')
|
132
|
+
# X-Axis - Vertical Lines
|
133
|
+
xml.g(:style => "color:grey",
|
134
|
+
:stroke => "currentColor",
|
135
|
+
:transform => ['translate(0,', ystart, ')'].join) do
|
136
|
+
|
137
|
+
xlines.each do |x, tl|
|
138
|
+
xml.path(:d => ['m', x, ' 0v', ylen].join)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
# X-Axis - Ticks
|
142
|
+
xml.g(:style => "color:black",
|
143
|
+
:stroke => "currentColor",
|
144
|
+
:transform => ['translate(0,', ystart, ')'].join) do
|
145
|
+
|
146
|
+
xlines.each do |x, mm|
|
147
|
+
tl = mm ? opts.long_tick_len : opts.short_tick_len # The tick length to set based on if the division is a major one
|
148
|
+
xml.path(:d => ['m', x, ' 0v', tl, 'm0 ', ylen-(2*tl), 'v', tl].join)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
# X-Axis - Labels
|
152
|
+
xml.g(:style => 'text-anchor:middle',
|
153
|
+
:stroke => 'none',
|
154
|
+
:transform => ['translate(0,', yend+2*opts.font_size, ')'].join,
|
155
|
+
'font-size' => [opts.font_size, 'pt'].join,
|
156
|
+
:fill => "#000") do
|
157
|
+
|
158
|
+
xlabels.each do |label|
|
159
|
+
xml.g(:transform => ['translate(', label[1], ')'].join) do
|
160
|
+
xml.text_ label[0]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
# X-Axis Text Label
|
165
|
+
xml.g(:style => 'text-anchor: middle',
|
166
|
+
:transform => ['translate(', xstart + xlen/2, ' ', opts.height - (opts.margins - opts.font_size), ')'].join,
|
167
|
+
'font-size' => [opts.label_font_size, 'pt'].join,
|
168
|
+
:fill => '#000') do
|
169
|
+
|
170
|
+
xml.text_ opts.x_text
|
171
|
+
end
|
172
|
+
# Y-Axis - Horizontal Lines
|
173
|
+
xml.g(:style => "color:grey",
|
174
|
+
:stroke => "currentColor",
|
175
|
+
:transform => ['translate(', xstart, ',0)'].join) do
|
176
|
+
|
177
|
+
ylines.each do |y, mm|
|
178
|
+
if mm # Only draw lines for major divisions
|
179
|
+
xml.path(:d => ['m0 ', y, 'h', xlen].join)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
# Y-Axis - Ticks
|
184
|
+
xml.g(:style => "color:black",
|
185
|
+
:stroke => "currentColor",
|
186
|
+
:transform => ['translate(', xstart, ',0)'].join) do
|
187
|
+
|
188
|
+
ylines.each do |y, mm|
|
189
|
+
tl = mm ? opts.long_tick_len : opts.short_tick_len # The tick length to set based on if the division is a major one
|
190
|
+
xml.path(:d => ['m0 ', y, 'h', tl, 'm', xlen-(2*tl), ' 0h', tl].join)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
# Y-Axis - Labels
|
194
|
+
xml.g(:style => 'text-anchor:end',
|
195
|
+
:stroke => 'none',
|
196
|
+
:transform => ['translate(', xstart-axis_padding, ',0)'].join,
|
197
|
+
'font-size' => [opts.font_size, 'pt'].join,
|
198
|
+
:fill => "#000") do
|
199
|
+
|
200
|
+
ylabels.each do |label|
|
201
|
+
xml.g(:transform => ['translate(0,', label[1]+(opts.font_size/2), ')'].join) do
|
202
|
+
xml.text_ label[0]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
# Y-Axis text label
|
207
|
+
xml.g(:style => 'text-anchor:middle',
|
208
|
+
:transform => ['translate(', opts.margins, ' ', ystart + ylen/2, ') rotate(-90)'].join,
|
209
|
+
'font-size' => [opts.label_font_size, 'pt'].join,
|
210
|
+
:fill => '#000') do
|
211
|
+
|
212
|
+
xml.text_ opts.y_text
|
213
|
+
end
|
214
|
+
# Bounding Box
|
215
|
+
xml.g(:stroke => "#333",
|
216
|
+
:fill => "none") do
|
217
|
+
xml.path(:d => ['m', xstart, ' ', ystart, 'v', ylen, 'h', xlen, 'v', -ylen , 'h', -xlen, 'z'].join)
|
218
|
+
end
|
219
|
+
# Plots
|
220
|
+
xml.a('xlink:title' => 'Plot #1') do
|
221
|
+
xml.g('stroke-width' => 1.4,
|
222
|
+
'stroke-linejoin' => 'bevel',
|
223
|
+
:fill => 'none') do
|
224
|
+
xml.path(:stroke => red.html, :d => plot)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
# No data marker
|
228
|
+
xml.g(:style => 'text-anchor:middle',
|
229
|
+
:stroke => red.html,
|
230
|
+
:transform => ['translate(', xstart + xlen/2, ',', ystart + ylen/2, ')'].join,
|
231
|
+
'font-size' => [opts.label_font_size+16, 'pt'].join,
|
232
|
+
:display => no_data_display,
|
233
|
+
:fill => red.html) do
|
234
|
+
|
235
|
+
xml.text_ 'No Data Found'
|
236
|
+
end
|
237
|
+
# An invisible rectangle to stop the final graph being highlighted and so on
|
238
|
+
xml.rect(:x => 0, :y => 0,
|
239
|
+
:width => opts.width,
|
240
|
+
:height => opts.height,
|
241
|
+
:fill => 'white',
|
242
|
+
'fill-opacity' => 0,
|
243
|
+
:stroke => 'none',
|
244
|
+
'stroke-width' => 0,
|
245
|
+
:onclick => 'null_handler')
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
# Output the XML
|
250
|
+
puts builder.to_xml
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "svg_plot_gen"
|
3
|
+
s.summary = "Creates the axes and gridlines for a graph in SVG. Perfect for creating a static background to which live data can be added."
|
4
|
+
s.version = "0.0.3"
|
5
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
6
|
+
s.author = "Richard Meadows"
|
7
|
+
s.homepage = "https://github.com/richardeoin/svg-plot-gen"
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.require_paths = ['lib']
|
11
|
+
s.executables = ['svg_plot_gen']
|
12
|
+
s.add_runtime_dependency 'trollop', ['~> 2']
|
13
|
+
s.add_runtime_dependency 'nokogiri', ['~> 1.5']
|
14
|
+
s.add_runtime_dependency 'color', ['~> 1.4']
|
15
|
+
s.license = 'MIT'
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: svg_plot_gen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Richard Meadows
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: trollop
|
16
|
+
requirement: &6204280 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *6204280
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nokogiri
|
27
|
+
requirement: &6203080 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.5'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *6203080
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: color
|
38
|
+
requirement: &6201340 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.4'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *6201340
|
47
|
+
description:
|
48
|
+
email:
|
49
|
+
executables:
|
50
|
+
- svg_plot_gen
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- Gemfile
|
55
|
+
- Gemfile.lock
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- example.svg
|
59
|
+
- lib/cursor_line.js
|
60
|
+
- lib/font.css
|
61
|
+
- lib/svg_plot_gen.rb
|
62
|
+
- svg_plot_gen.gemspec
|
63
|
+
- bin/svg_plot_gen
|
64
|
+
homepage: https://github.com/richardeoin/svg-plot-gen
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.11
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Creates the axes and gridlines for a graph in SVG. Perfect for creating a
|
89
|
+
static background to which live data can be added.
|
90
|
+
test_files: []
|