ruby-charts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/ruby_charts/charts/bar_chart.rb +146 -0
- data/lib/ruby_charts/charts/base_chart.rb +120 -0
- data/lib/ruby_charts/charts/line_chart.rb +84 -0
- data/lib/ruby_charts/charts/pie_chart.rb +81 -0
- data/lib/ruby_charts/data_loader.rb +49 -0
- data/lib/ruby_charts.rb +90 -0
- metadata +74 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c3986814fddda6f6d9676fe18ae7902e5f4e0adf11a85d5f02449a423ae7b87a
|
|
4
|
+
data.tar.gz: 7fab1d76371520a89cae1986662d79925fcbcfddc19937ccf01c255890feb2f7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 92b831ad1da8ab3f5a4510bf5193240eebdd1adbb8c2f85ea0ce34b7b50e4a63c322858223a8179694055d7d87cc0518ff498866f36beca77fb1b30e33a47b46
|
|
7
|
+
data.tar.gz: 9645e4e4c0c69d1a238fa200c574bebcb8b9fd9b9980fdf049482fbbeab00ff039869644f91b64aad9d993a86ea5e224941c62276fa7d9433c00573656ad8cec
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# app/lib/ruby_charts/charts/bar_chart.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Charts
|
|
5
|
+
class BarChart < BaseChart
|
|
6
|
+
def initialize(data, orientation = :vertical)
|
|
7
|
+
super(data)
|
|
8
|
+
@orientation = orientation
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def horizontal!
|
|
12
|
+
@orientation = :horizontal
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def vertical!
|
|
17
|
+
@orientation = :vertical
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def draw_chart_content
|
|
24
|
+
if @orientation == :horizontal
|
|
25
|
+
draw_horizontal_bars
|
|
26
|
+
else
|
|
27
|
+
draw_vertical_bars
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def draw_vertical_bars
|
|
32
|
+
chart_width = @options[:width] - (2 * @options[:padding])
|
|
33
|
+
chart_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
34
|
+
|
|
35
|
+
max_value = @data[:values].max
|
|
36
|
+
bar_width = chart_width / @data[:labels].length * 0.7
|
|
37
|
+
spacing = chart_width / @data[:labels].length
|
|
38
|
+
|
|
39
|
+
@data[:labels].each_with_index do |label, index|
|
|
40
|
+
value = @data[:values][index]
|
|
41
|
+
bar_height = (value / max_value.to_f) * chart_height
|
|
42
|
+
|
|
43
|
+
x1 = @options[:padding] + (index * spacing) + ((spacing - bar_width) / 2)
|
|
44
|
+
y1 = @options[:height] - @options[:padding] - bar_height.to_i
|
|
45
|
+
x2 = x1 + bar_width.to_i
|
|
46
|
+
y2 = @options[:height] - @options[:padding]
|
|
47
|
+
|
|
48
|
+
color = hex_to_rgb(@options[:colors][index % @options[:colors].length])
|
|
49
|
+
|
|
50
|
+
# Draw bar
|
|
51
|
+
@img.filled_rectangle(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
|
|
52
|
+
|
|
53
|
+
# Draw label
|
|
54
|
+
@img.text(label, {
|
|
55
|
+
x: x1.to_i + 5,
|
|
56
|
+
y: y2 + 10,
|
|
57
|
+
size: 10,
|
|
58
|
+
color: [50, 50, 50]
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
# Draw value on top
|
|
62
|
+
if @options[:label_format] == :percentage
|
|
63
|
+
label_text = "#{(value / @data[:values].sum.to_f * 100).round(1)}%"
|
|
64
|
+
else
|
|
65
|
+
label_text = value.to_s
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@img.text(label_text, {
|
|
69
|
+
x: x1.to_i + 10,
|
|
70
|
+
y: y1 - 15,
|
|
71
|
+
size: 10,
|
|
72
|
+
color: [50, 50, 50]
|
|
73
|
+
})
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Draw axes
|
|
77
|
+
draw_axes
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def draw_horizontal_bars
|
|
81
|
+
chart_width = @options[:width] - (2 * @options[:padding])
|
|
82
|
+
chart_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
83
|
+
|
|
84
|
+
max_value = @data[:values].max
|
|
85
|
+
bar_height = chart_height / @data[:labels].length * 0.7
|
|
86
|
+
spacing = chart_height / @data[:labels].length
|
|
87
|
+
|
|
88
|
+
@data[:labels].each_with_index do |label, index|
|
|
89
|
+
value = @data[:values][index]
|
|
90
|
+
bar_width = (value / max_value.to_f) * chart_width
|
|
91
|
+
|
|
92
|
+
y1 = @options[:padding] + (index * spacing) + ((spacing - bar_height) / 2)
|
|
93
|
+
x1 = @options[:padding]
|
|
94
|
+
y2 = y1 + bar_height.to_i
|
|
95
|
+
x2 = x1 + bar_width.to_i
|
|
96
|
+
|
|
97
|
+
color = hex_to_rgb(@options[:colors][index % @options[:colors].length])
|
|
98
|
+
|
|
99
|
+
# Draw bar
|
|
100
|
+
@img.filled_rectangle(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
|
|
101
|
+
|
|
102
|
+
# Draw label
|
|
103
|
+
@img.text(label, {
|
|
104
|
+
x: @options[:padding] - 70,
|
|
105
|
+
y: y1.to_i + 5,
|
|
106
|
+
size: 10,
|
|
107
|
+
color: [50, 50, 50]
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
# Draw value
|
|
111
|
+
@img.text(value.to_s, {
|
|
112
|
+
x: x2 + 5,
|
|
113
|
+
y: y1.to_i + 5,
|
|
114
|
+
size: 10,
|
|
115
|
+
color: [50, 50, 50]
|
|
116
|
+
})
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def draw_axes
|
|
121
|
+
# X axis
|
|
122
|
+
@img.line(
|
|
123
|
+
@options[:padding],
|
|
124
|
+
@options[:height] - @options[:padding],
|
|
125
|
+
@options[:width] - @options[:padding],
|
|
126
|
+
@options[:height] - @options[:padding],
|
|
127
|
+
[0, 0, 0]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Y axis
|
|
131
|
+
@img.line(
|
|
132
|
+
@options[:padding],
|
|
133
|
+
@options[:padding],
|
|
134
|
+
@options[:padding],
|
|
135
|
+
@options[:height] - @options[:padding],
|
|
136
|
+
[0, 0, 0]
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def hex_to_rgb(hex)
|
|
141
|
+
hex = hex.delete('#')
|
|
142
|
+
[hex[0..1], hex[2..3], hex[4..5]].map { |x| x.to_i(16) }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# app/lib/ruby_charts/charts/base_chart.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Charts
|
|
5
|
+
class BaseChart
|
|
6
|
+
attr_accessor :data, :options
|
|
7
|
+
|
|
8
|
+
def initialize(data)
|
|
9
|
+
@data = data
|
|
10
|
+
@options = {
|
|
11
|
+
width: 1200,
|
|
12
|
+
height: 700,
|
|
13
|
+
title: 'Chart',
|
|
14
|
+
subtitle: nil,
|
|
15
|
+
labels: true,
|
|
16
|
+
colors: default_colors,
|
|
17
|
+
padding: 80,
|
|
18
|
+
font_size: 12
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def title(text)
|
|
23
|
+
@options[:title] = text
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def subtitle(text)
|
|
28
|
+
@options[:subtitle] = text
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def width(w)
|
|
33
|
+
@options[:width] = w
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def height(h)
|
|
38
|
+
@options[:height] = h
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def colors(color_array)
|
|
43
|
+
@options[:colors] = color_array
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def labels(format: :text)
|
|
48
|
+
@options[:label_format] = format
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def legend(position: :right)
|
|
53
|
+
@options[:legend] = position
|
|
54
|
+
self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def render
|
|
58
|
+
@img = GD::Image.new(@options[:width], @options[:height])
|
|
59
|
+
@img.fill([255, 255, 255])
|
|
60
|
+
|
|
61
|
+
draw_background
|
|
62
|
+
draw_title
|
|
63
|
+
draw_chart_content
|
|
64
|
+
draw_legend if @options[:legend]
|
|
65
|
+
|
|
66
|
+
@img
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def save_png(filename)
|
|
70
|
+
render.save_png(filename)
|
|
71
|
+
puts "✓ Chart saved: #{filename}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def save_pdf(filename)
|
|
75
|
+
# Requires ruby-pdf integration
|
|
76
|
+
render_to_pdf(filename)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def default_colors
|
|
82
|
+
[
|
|
83
|
+
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8',
|
|
84
|
+
'#F7DC6F', '#BB8FCE', '#85C1E2', '#F8B88B', '#ABEBC6'
|
|
85
|
+
]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def draw_background
|
|
89
|
+
@img.filled_rectangle(0, 0, @options[:width] - 1, @options[:height] - 1, [255, 255, 255])
|
|
90
|
+
|
|
91
|
+
# Subtle border
|
|
92
|
+
@img.rectangle(0, 0, @options[:width] - 1, @options[:height] - 1, [200, 200, 200])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def draw_title
|
|
96
|
+
# Main title
|
|
97
|
+
@img.text(@options[:title], {
|
|
98
|
+
x: @options[:padding],
|
|
99
|
+
y: 20,
|
|
100
|
+
size: 24,
|
|
101
|
+
color: [30, 30, 30]
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
# Subtitle
|
|
105
|
+
if @options[:subtitle]
|
|
106
|
+
@img.text(@options[:subtitle], {
|
|
107
|
+
x: @options[:padding],
|
|
108
|
+
y: 50,
|
|
109
|
+
size: 14,
|
|
110
|
+
color: [100, 100, 100]
|
|
111
|
+
})
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def draw_chart_content
|
|
116
|
+
raise NotImplementedError, "Subclasses must implement draw_chart_content"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# app/lib/ruby_charts/charts/line_chart.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Charts
|
|
5
|
+
class LineChart < BaseChart
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def draw_chart_content
|
|
9
|
+
chart_width = @options[:width] - (2 * @options[:padding])
|
|
10
|
+
chart_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
11
|
+
|
|
12
|
+
max_value = @data[:values].max
|
|
13
|
+
min_value = @data[:values].min
|
|
14
|
+
range = max_value - min_value
|
|
15
|
+
|
|
16
|
+
# Draw grid
|
|
17
|
+
draw_grid(chart_width, chart_height, max_value, min_value)
|
|
18
|
+
|
|
19
|
+
# Draw line
|
|
20
|
+
points = @data[:values].each_with_index.map do |value, index|
|
|
21
|
+
x = @options[:padding] + (index * chart_width / (@data[:values].length - 1))
|
|
22
|
+
y = @options[:height] - @options[:padding] - ((value - min_value) / range.to_f * chart_height)
|
|
23
|
+
[x.to_i, y.to_i]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
color = hex_to_rgb(@options[:colors].first)
|
|
27
|
+
|
|
28
|
+
# Draw line segments
|
|
29
|
+
points.each_cons(2) do |p1, p2|
|
|
30
|
+
@img.line(p1[0], p1[1], p2[0], p2[1], color)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Draw points
|
|
34
|
+
points.each_with_index do |point, index|
|
|
35
|
+
@img.filled_circle(point[0], point[1], 4, color)
|
|
36
|
+
|
|
37
|
+
# Draw label
|
|
38
|
+
@img.text(@data[:labels][index], {
|
|
39
|
+
x: point[0] - 20,
|
|
40
|
+
y: point[1] - 20,
|
|
41
|
+
size: 9,
|
|
42
|
+
color: [50, 50, 50]
|
|
43
|
+
})
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Draw axes
|
|
47
|
+
draw_axes(max_value, min_value)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def draw_grid(width, height, max_val, min_val)
|
|
51
|
+
# Horizontal grid lines
|
|
52
|
+
5.times do |i|
|
|
53
|
+
y = @options[:padding] + 50 + (i * height / 4)
|
|
54
|
+
@img.line(@options[:padding], y, @options[:width] - @options[:padding], y, [230, 230, 230])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Vertical grid lines
|
|
58
|
+
(@data[:values].length - 1).times do |i|
|
|
59
|
+
x = @options[:padding] + (i * width / (@data[:values].length - 1))
|
|
60
|
+
@img.line(x, @options[:padding] + 50, x, @options[:height] - @options[:padding], [230, 230, 230])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def draw_axes(max_val, min_val)
|
|
65
|
+
# Y axis labels
|
|
66
|
+
5.times do |i|
|
|
67
|
+
value = min_val + ((max_val - min_val) * i / 4)
|
|
68
|
+
y = @options[:height] - @options[:padding] - (i * (@options[:height] - 2 * @options[:padding] - 100) / 4)
|
|
69
|
+
@img.text(value.to_i.to_s, {
|
|
70
|
+
x: 30,
|
|
71
|
+
y: y.to_i - 7,
|
|
72
|
+
size: 9,
|
|
73
|
+
color: [100, 100, 100]
|
|
74
|
+
})
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def hex_to_rgb(hex)
|
|
79
|
+
hex = hex.delete('#')
|
|
80
|
+
[hex[0..1], hex[2..3], hex[4..5]].map { |x| x.to_i(16) }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# app/lib/ruby_charts/charts/pie_chart.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
module Charts
|
|
5
|
+
class PieChart < BaseChart
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def draw_chart_content
|
|
9
|
+
chart_area_width = @options[:width] - (2 * @options[:padding])
|
|
10
|
+
chart_area_height = @options[:height] - (2 * @options[:padding]) - 100
|
|
11
|
+
|
|
12
|
+
center_x = @options[:padding] + (chart_area_width / 2)
|
|
13
|
+
center_y = @options[:padding] + 50 + (chart_area_height / 2)
|
|
14
|
+
radius = [chart_area_width, chart_area_height].min / 3
|
|
15
|
+
|
|
16
|
+
total = @data[:values].sum
|
|
17
|
+
current_angle = 0
|
|
18
|
+
|
|
19
|
+
@data[:labels].each_with_index do |label, index|
|
|
20
|
+
value = @data[:values][index]
|
|
21
|
+
percentage = (value / total.to_f) * 100
|
|
22
|
+
angle = (percentage / 100.0) * 360
|
|
23
|
+
|
|
24
|
+
color = hex_to_rgb(@options[:colors][index % @options[:colors].length])
|
|
25
|
+
|
|
26
|
+
# Draw pie slice
|
|
27
|
+
draw_pie_slice(center_x, center_y, radius, current_angle, angle, color)
|
|
28
|
+
|
|
29
|
+
# Draw label
|
|
30
|
+
if @options[:label_format] == :percentage
|
|
31
|
+
label_text = "#{percentage.round(1)}%"
|
|
32
|
+
else
|
|
33
|
+
label_text = "#{label}: #{value}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
draw_pie_label(center_x, center_y, radius, current_angle, angle, label_text)
|
|
37
|
+
|
|
38
|
+
current_angle += angle
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def draw_pie_slice(cx, cy, radius, start_angle, angle, color)
|
|
43
|
+
# Draw filled circle slice
|
|
44
|
+
# Using ruby-libgd polygon
|
|
45
|
+
points = [[cx, cy]]
|
|
46
|
+
|
|
47
|
+
steps = (angle / 5).ceil # 5-degree increments for smooth curve
|
|
48
|
+
(0..steps).each do |i|
|
|
49
|
+
current_angle = start_angle + (angle * i / steps)
|
|
50
|
+
rad = (current_angle * Math::PI / 180)
|
|
51
|
+
x = (cx + radius * Math.cos(rad)).to_i
|
|
52
|
+
y = (cy + radius * Math.sin(rad)).to_i
|
|
53
|
+
points << [x, y]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@img.filled_polygon(points, color)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def draw_pie_label(cx, cy, radius, start_angle, angle, text)
|
|
60
|
+
label_angle = start_angle + (angle / 2)
|
|
61
|
+
label_radius = radius * 0.65
|
|
62
|
+
|
|
63
|
+
rad = (label_angle * Math::PI / 180)
|
|
64
|
+
label_x = (cx + label_radius * Math.cos(rad)).to_i
|
|
65
|
+
label_y = (cy + label_radius * Math.sin(rad)).to_i
|
|
66
|
+
|
|
67
|
+
@img.text(text, {
|
|
68
|
+
x: label_x - 30,
|
|
69
|
+
y: label_y - 7,
|
|
70
|
+
size: 11,
|
|
71
|
+
color: [255, 255, 255]
|
|
72
|
+
})
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def hex_to_rgb(hex)
|
|
76
|
+
hex = hex.delete('#')
|
|
77
|
+
[hex[0..1], hex[2..3], hex[4..5]].map { |x| x.to_i(16) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# app/lib/ruby_charts/data_loader.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
class DataLoader
|
|
5
|
+
def self.from_csv(filepath, options = {})
|
|
6
|
+
data = CSV.read(filepath, headers: true)
|
|
7
|
+
parse_data(data, options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.from_xlsx(filepath, options = {})
|
|
11
|
+
require 'roo'
|
|
12
|
+
workbook = Roo::Excelx.new(filepath)
|
|
13
|
+
sheet = options[:sheet] || workbook.default_sheet
|
|
14
|
+
workbook.sheet(sheet)
|
|
15
|
+
|
|
16
|
+
headers = workbook.row(1)
|
|
17
|
+
rows = (2..workbook.last_row).map { |i| workbook.row(i) }
|
|
18
|
+
|
|
19
|
+
parse_rows(headers, rows, options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.from_yaml(filepath, options = {})
|
|
23
|
+
data = YAML.safe_load_file(filepath)
|
|
24
|
+
parse_data(data, options)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def self.parse_data(data, options)
|
|
30
|
+
{
|
|
31
|
+
labels: data.map { |row| row[0] },
|
|
32
|
+
values: data.map { |row| row[1] },
|
|
33
|
+
series: extract_series(data, options)
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.extract_series(data, options)
|
|
38
|
+
# Para multi-series charts
|
|
39
|
+
if data.first.is_a?(Hash) && data.first.keys.length > 2
|
|
40
|
+
data.first.keys.map do |key|
|
|
41
|
+
{
|
|
42
|
+
name: key,
|
|
43
|
+
values: data.map { |row| row[key] }
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/ruby_charts.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# app/lib/ruby_charts.rb
|
|
2
|
+
|
|
3
|
+
module RubyCharts
|
|
4
|
+
class ChartBuilder
|
|
5
|
+
def initialize(data)
|
|
6
|
+
@data = data
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.from_csv(filepath, options = {})
|
|
10
|
+
data = DataLoader.from_csv(filepath, options)
|
|
11
|
+
new(data)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.from_xlsx(filepath, options = {})
|
|
15
|
+
data = DataLoader.from_xlsx(filepath, options)
|
|
16
|
+
new(data)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.from_yaml(filepath, options = {})
|
|
20
|
+
data = DataLoader.from_yaml(filepath, options)
|
|
21
|
+
new(data)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.from_hash(data_hash)
|
|
25
|
+
new(data_hash)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def type(chart_type)
|
|
29
|
+
@chart_type = chart_type
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def title(text)
|
|
34
|
+
@title = text
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def subtitle(text)
|
|
39
|
+
@subtitle = text
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def colors(*color_array)
|
|
44
|
+
@colors = color_array.flatten
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def labels(format: :text)
|
|
49
|
+
@label_format = format
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def legend(position: :right)
|
|
54
|
+
@legend = position
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def save_png(filename)
|
|
59
|
+
chart = build_chart
|
|
60
|
+
chart.title(@title) if @title
|
|
61
|
+
chart.subtitle(@subtitle) if @subtitle
|
|
62
|
+
chart.colors(@colors) if @colors
|
|
63
|
+
chart.labels(format: @label_format) if @label_format
|
|
64
|
+
chart.legend(position: @legend) if @legend
|
|
65
|
+
chart.save_png(filename)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def save_pdf(filename)
|
|
69
|
+
chart = build_chart
|
|
70
|
+
chart.save_pdf(filename)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def build_chart
|
|
76
|
+
case @chart_type
|
|
77
|
+
when :pie
|
|
78
|
+
Charts::PieChart.new(@data)
|
|
79
|
+
when :bar, :vertical_bar
|
|
80
|
+
Charts::BarChart.new(@data, :vertical)
|
|
81
|
+
when :horizontal_bar
|
|
82
|
+
Charts::BarChart.new(@data, :horizontal)
|
|
83
|
+
when :line
|
|
84
|
+
Charts::LineChart.new(@data)
|
|
85
|
+
else
|
|
86
|
+
raise "Unknown chart type: #{@chart_type}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby-charts
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Germán Silva
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ruby-libgd
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.3.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.3.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: roo
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 2.10.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 2.10.0
|
|
40
|
+
description: Generate publication-ready pie, bar, and line charts from data files
|
|
41
|
+
email:
|
|
42
|
+
- ggerman@gmail.com
|
|
43
|
+
executables: []
|
|
44
|
+
extensions: []
|
|
45
|
+
extra_rdoc_files: []
|
|
46
|
+
files:
|
|
47
|
+
- lib/ruby_charts.rb
|
|
48
|
+
- lib/ruby_charts/charts/bar_chart.rb
|
|
49
|
+
- lib/ruby_charts/charts/base_chart.rb
|
|
50
|
+
- lib/ruby_charts/charts/line_chart.rb
|
|
51
|
+
- lib/ruby_charts/charts/pie_chart.rb
|
|
52
|
+
- lib/ruby_charts/data_loader.rb
|
|
53
|
+
homepage: https://github.com/ggerman/ruby-charts
|
|
54
|
+
licenses:
|
|
55
|
+
- MIT
|
|
56
|
+
metadata: {}
|
|
57
|
+
rdoc_options: []
|
|
58
|
+
require_paths:
|
|
59
|
+
- lib
|
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: 2.7.0
|
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
requirements: []
|
|
71
|
+
rubygems_version: 4.0.3
|
|
72
|
+
specification_version: 4
|
|
73
|
+
summary: Professional charts from CSV/XLSX/YAML using ruby-libgd
|
|
74
|
+
test_files: []
|