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 ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'nokogiri'
4
+ gem 'color'
5
+ gem 'trollop'
data/Gemfile.lock ADDED
@@ -0,0 +1,14 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ color (1.4.1)
5
+ nokogiri (1.5.6)
6
+ trollop (2.0)
7
+
8
+ PLATFORMS
9
+ ruby
10
+
11
+ DEPENDENCIES
12
+ color
13
+ nokogiri
14
+ trollop
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
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'svg_plot_gen'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'svg_plot_gen'
8
+ end
9
+
10
+ Svg_Plot_Gen.gen()
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>
@@ -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
@@ -0,0 +1,6 @@
1
+ @font-face {
2
+ font-family: 'Exo';
3
+ font-style: normal;
4
+ font-weight: 400;
5
+ src: local('Exo Regular'), local('Exo-Regular'), url(http://themes.googleusercontent.com/static/fonts/exo/v1/wocyXRLWPo2Av-yUTmmbTA.woff) format('woff');
6
+ }
@@ -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: []