All these examples are generated using spec/readme/technical_readme_spec.rb . There are some differences: filename is without path and is used rmagick renderer. Github has issues with SVG files.
In this documentation I lowered default graph size so it looks nicer here. If you would like to do this insert something like this at the beginning of your code:
GraphImageDrawer.width = 400
GraphImageDrawer.height = 300
First thing we need to have data to show on graph, for example something like:
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
If you want to put this data on graph you need to create TechnicalGraph object
@tg = TechnicalGraph.new
and add a layer.
@tg.add_layer(@simple_data_array)
We added data, but we don’t see anything. Now we have to render graph and save it to file.
file_name = '01_simplest.png'
@tg.save_to_file(file_name)
And we got our first graph with one layer, without changing any options, a bit raw.
Maybe example with two layers?
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@simple_data_array_b = [
{ :x => 0.5, :y => 0.5 },
{ :x => 1.5, :y => 0.5 },
{ :x => 2.5, :y => 1.5 },
{ :x => 3.5, :y => 1.0 },
{ :x => 4.5, :y => 1.5 },
{ :x => 5.5, :y => 1.5 },
]
@tg = TechnicalGraph.new
@tg.add_layer(@simple_data_array)
@tg.add_layer(@simple_data_array_b)
file_name = '02_two_layers.png'
@tg.save_to_file(file_name)
By default ranges are calculated automatically so all points are visible. You can override ranges setting.
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:x_min => -2,
:x_max => 10,
:y_min => -1,
:y_max => 10,
})
@tg.add_layer(@simple_data_array)
file_name = '03_changed_ranges.png'
@tg.save_to_file(file_name)
Keep in mind that ranges can be changed while rendering graph, but you can be sure – they can be only enlarged and all points will be visible. Unless…
You can turn off automatic range enlargement algorithm by using:
options[:xy_behaviour] = :fixed
Example:
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:x_min => 1,
:x_max => 2,
:y_min => 1,
:y_max => 2,
:xy_behaviour => :fixed,
})
@tg.add_layer(@simple_data_array)
file_name = '04_changed_ranges_fixed.png'
@tg.save_to_file(file_name)
Axis can be calculated using two algorithms:
- fixed interval – axis are placed every precise distance,
- fixed amount – there is fixed amount of axis on graph.
Keep in mind that there is X (parameter) and Y (value) axis. If you want to set fixed amount, you should set options[:x_axis_fixed_interval] and/or options[:y_axis_fixed_interval] to false, like in the example below.
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:x_axis_fixed_interval => false,
:y_axis_fixed_interval => false,
:y_axis_count => 20,
:x_axis_count => 20,
})
@tg.add_layer(@simple_data_array)
file_name = '05_axis_fixed_amount.png'
@tg.save_to_file(file_name)
Axis can be calculated using two algorithms:
- fixed interval – axis is every some distance,
- fixed count – there is fixed amount of axis on graph.
Keep in mind that where is X (parameter) and Y (value) axis. If you want to set fixed interval you should set options[:x_axis_fixed_interval] and/or options[:y_axis_fixed_interval] to true, like in the example below.
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:x_axis_fixed_interval => true,
:y_axis_fixed_interval => true,
:y_axis_interval => 0.8,
:x_axis_interval => 0.6,
})
@tg.add_layer(@simple_data_array)
file_name = '06_axis_fixed_interval.png'
@tg.save_to_file(file_name)
You can add label to X and Y axis. using options[:x_axis_label] and options[:y_axis_label], and you can choose font size using options[:axis_label_font_size].
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:x_axis_label => 'parameter',
:y_axis_label => 'value',
:axis_label_font_size => 16
})
@tg.add_layer(@simple_data_array)
file_name = '07_axis_label.png'
@tg.save_to_file(file_name)
By default all float number are shown with two digits after dot precision. You can override it using options[:truncate_string] like in example below. If you want to have values near graph points you can use proper layer options.
Layers have also options, and it is useful for this example to turn on value labels to true – layer_options[:value_labels] = true .
@float_data_array = [
{ :x => 0, :y => 0 },
{ :x => 0.111, :y => 0.123 },
{ :x => 0.222, :y => 1.456 },
{ :x => 0.333, :y => 2.8756 },
{ :x => 0.555, :y => 1.042 },
{ :x => 0.888, :y => 0.988 },
]
@tg = TechnicalGraph.new(
{
:truncate_string => "%.3f"
})
@layer_params = {
:value_labels => true
}
@tg.add_layer(@float_data_array, @layer_params)
file_name = '08a_truncate_string.png'
@tg.save_to_file(file_name)
@float_data_array = [
{ :x => 0, :y => 0 },
{ :x => 0.111, :y => 0.123 },
{ :x => 0.222, :y => 1.456 },
{ :x => 0.333, :y => 2.8756 },
{ :x => 0.555, :y => 1.042 },
{ :x => 0.888, :y => 0.988 },
]
@tg = TechnicalGraph.new(
{
:truncate_string => "%.1f"
})
@layer_params = {
:value_labels => true
}
@tg.add_layer(@float_data_array, @layer_params)
file_name = '08b_truncate_string.png'
@tg.save_to_file(file_name)
It would be very silly if this library had hard coded image size. You can change it using options[:width] and options[:height].
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:width => 400,
:height => 300
})
@tg.add_layer(@simple_data_array)
file_name = '09_image_size.png'
@tg.save_to_file(file_name)
If you think you have better artistic taste feel free to change colours used in graph :) Keep in mind that hatch color is available only in rmagick renderer. Some short info about renderers is located in README.
There are three color options per graph:
- options[:background_color] – background color of image
- options[:background_hatch_color] – background hatch color, used only in RMagick renderer
- options[:axis_color] – color of axis, default #000000
And one layer options:
- layer_options[:color] – it is color of layer
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:background_color => '#000000',
:background_hatch_color => '#222222',
:axis_color => '#FFFFFF'
})
@tg.add_layer(@simple_data_array, {:color => '#0000FF'})
file_name = '10_colors.png'
@tg.save_to_file(file_name)
There were two render classes since version 0.4.0: Rasem and RMagick. Now it is more complicated.
If you want SVG file you a very happy man, everything works and SVG looks good. If you want PNG you can use chunky_png but you are missing some important features like texts, dots, … You can install rmagick and technical_graph will use power of rmagick to create very nice PNG graphs.
Anti-aliasing is only available using RMagick renderer. By default it is disabled.
options[:antialias] = true
Sample graph without anti-aliasing has 11kB, with 69kB. Does size matter to you?
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:antialias => true,
:drawer_class => :rmagick
})
@tg.add_layer(@simple_data_array)
file_name = '12_aa_true.png'
@tg.save_to_file(file_name)
# only for size comparison
@tg = TechnicalGraph.new(
{
:antialias => false,
:drawer_class => :rmagick
})
@tg.add_layer(@simple_data_array)
file_name = '12_aa_false.png'
@tg.save_to_file(file_name)
With anti-alias
Without anti-alias
You can set various font sizes.
- options[:layers_font_size] – size of font used for values in graph, keep in mind that layer_options[:value_labels] must be enabled to see any result
- options[:axis_font_size] – size of font used in axis
- options[:axis_label_font_size] – size of font used in options[:x_axis_label] and options[:y_axis_label], to see any result at least one of them must be set
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@tg = TechnicalGraph.new(
{
:x_axis_label => 'parameter',
:y_axis_label => 'value',
:layers_font_size => 14,
:axis_font_size => 18,
:axis_label_font_size => 24
})
@tg.add_layer(@simple_data_array, {:value_labels => true})
file_name = '13_font_sizes.png'
@tg.save_to_file(file_name)
If you want to use legend you only have to set labels for layers and enable it by using:
- options[:legend] – set to true to enable legend, by default auto position mode is enabled
- layer_options[:label] – label used for legend
Position of legend is calculated magically – 8 corner places are checked for distance to all graph points. Point which has longest distance to graph points is used for legend position.
Every layer has color which can be set by user using code below. If you don’t set there is a bank of some fixed colors and random color generator.
layer_options[:color] = '#FF0000' # color in format #RRGGBB
Graph sample code:
@simple_data_array = [
{ :x => 0, :y => 0 },
{ :x => 1, :y => 1 },
{ :x => 2, :y => 2 },
{ :x => 3, :y => 2 },
{ :x => 4, :y => 1 },
{ :x => 5, :y => 0 },
]
@simple_data_array_second = @simple_data_array.collect{|a| {:x => a[:x] + 0.31, :y => a[:y] + 0.21 }}
@simple_data_array_third = @simple_data_array.collect{|a| {:x => a[:x] * 0.99 + 0.23, :y => a[:y] * 1.2 - 0.12 }}
@tg = TechnicalGraph.new(
{
:legend => true
})
@tg.add_layer(@simple_data_array, {:label => 'simple', :color => '#AA7722'})
@tg.add_layer(@simple_data_array_second, {:label => 'offset', :color => '#00AA55'})
@tg.add_layer(@simple_data_array_third, {:label => 'scaled', :color => '#3322BB'})
file_name = '14_simple_legend.png'
@tg.save_to_file(file_name)
If you like you can turn off auto position:
options[:legend_auto] = false
options[:legend_x] = 100
options[:legend_y] = 100
There are also other useful parameters
- options[:legend_width] – width of longest label in pixels used for setting proper distance while drawing on right border, default is 100 and it is enlarged auto. using font size
- options[:legend_margin] – graph margin used not to draw legend on border, default 50
Useful when origin of data is a bit noisy. For best results you can add two layers: one raw, and second with smoothing enabled.
@tg = TechnicalGraph.new(
{
:width => 1000,
:height => 750,
:legend => true,
:x_axis_label => "Parameter",
:y_axis_label => "Value"
}
)
max = 100
@layer_data = Array.new
(0..max).each do |i|
x = -10.0 + (20.0 * i.to_f / max.to_f)
y = 10.0 * Math.cos(i.to_f * (2.0 * 3.14 / max.to_f))
y += rand * 2.0
@layer_data << { :x => x, :y => y }
end
# adding simple layer
@layer_params = {
:label => 'raw',
:value_labels => false,
:simple_smoother => false,
:simple_smoother_level => 1,
:simple_smoother_strategy => :gauss,
:simple_smoother_x => false,
}
@layer_params_b = @layer_params.clone.merge(
{
:label => 'smoothed - level 3',
:simple_smoother_level => 3,
:simple_smoother => true
})
@layer_params_e = @layer_params.clone.merge(
{
:label => 'smoothed - level 50',
:simple_smoother_level => 50,
:simple_smoother => true
})
@tg.add_layer(@layer_data.clone, @layer_params)
@tg.add_layer(@layer_data.clone, @layer_params_b)
@tg.add_layer(@layer_data.clone, @layer_params_e)
file_name = '15_smoothing.png'
@tg.save_to_file(file_name)
Useful when origin of data is a bit noisy. For best results you can add two layers: one raw, and second with noise removal. It can be used with smoothing.
@tg = TechnicalGraph.new(
{
:legend => true
}
)
max = 250
@layer_data = Array.new
(0..max).each do |i|
x = -10.0 + (20.0 * i.to_f / max.to_f)
y = -10.0 + (20.0 * i.to_f / max.to_f)
@layer_data << { :x => x, :y => y }
end
# custom spikes
[3, 36, 99, 187, 204].each do |i|
offset = 5.0
offset *= -1.0 if rand(100) % 2 == 1
@layer_data[i][:y] += offset
end
# some random spikes
150.times do
i = rand(@layer_data.size)
offset = rand(80).to_f / 10.0
offset *= -1.0 if rand(100) % 2 == 1
@layer_data[i][:y] += offset
end
# adding simple layer
@layer_params = {
:label => 'raw',
:value_labels => false,
}
@layer_params_b = @layer_params.clone.merge(
{
:label => 'n.r. level 3, window 10',
:noise_removal_window_size => 10,
:noise_removal_level => 3,
:noise_removal => true
})
@layer_params_e = @layer_params.clone.merge(
{
:label => 'n.r level 10, window 30',
:noise_removal_window_size => 10,
:noise_removal_level => 30,
:noise_removal => true
})
@tg.add_layer(@layer_data.clone, @layer_params)
@tg.add_layer(@layer_data.clone, @layer_params_b)
@tg.add_layer(@layer_data.clone, @layer_params_e)
file_name = '16_noise_removal.png'
@tg.save_to_file(file_name)
In case of large amount of data axis can be squeezed and graph is unreadable. You can set axis enlargement to help in these situations. When distance between axis is lower than set in options[:x_axis_min_distance] or options[:y_axis_min_distance] image dimensions are enlarged to maintain proper axis distances. There is default distance for X and Y axis of 30 pixels.
Possible options:
- options[:axis_density_enlarge_image] – enable both axis (X, Y)
- options[:x_axis_density_enlarge_image] – enable only X axis
- options[:y_axis_density_enlarge_image] – enable only Y axis
- options[:x_axis_min_distance] – minimum distance for X axis
- options[:y_axis_min_distance] – minimum distance for Y axis
@tg = TechnicalGraph.new(
{
:x_axis_density_enlarge_image => true,
:x_axis_min_distance => 30,
:x_axis_interval => 1.0,
:x_axis_fixed_interval => true,
:width => 100
}
)
max = 20
@layer_data = Array.new
(0..max).each do |i|
x = -10.0 + (20.0 * i.to_f / max.to_f)
y = -10.0 + rand(2000).to_f / 100.0
@layer_data << { :x => x, :y => y }
end
@tg.add_layer(@layer_data)
file_name = '17_axis_enlargement.png'
@tg.save_to_file(file_name)
The best way is to experiment with all parameters and choose which fit best.
Possible options:
- layer_options[:simple_smoother] – set true to enable
- layer_options[:simple_smoother_level] – higher → more smooth
- layer_options[:simple_smoother_x] – use X offset smoothing, slower but a little better quality
@tg = TechnicalGraph.new(
{
:width => 1600,
:height => 1200,
:legend => true,
:x_axis_label => "Parameter",
:y_axis_label => "Value",
:drawer_class => README_RENDERED,
}
)
max = 200
@layer_data = Array.new
(0..max).each do |i|
x = -10.0 + (20.0 * i.to_f / max.to_f)
y = 10.0 * Math.cos(i.to_f * (2.0 * 3.14 / max.to_f))
y += rand * 2.0
@layer_data << { :x => x, :y => y }
end
# adding simple layer
@layer_params = {
:label => 'raw',
:value_labels => false
}
@layer_params_smooth_wox = @layer_params.clone.merge(
{
:label => 'without X smoothing',
:simple_smoother => true,
:simple_smoother_level => 5,
:simple_smoother_strategy => :gauss,
:simple_smoother_x => false,
}
)
@layer_params_smooth_wx = @layer_params.clone.merge(
{
:label => 'with X smoothing',
:simple_smoother => true,
:simple_smoother_level => 5,
:simple_smoother_x => true,
}
)
@tg.add_layer(@layer_data.clone, @layer_params_smooth_wox)
@tg.add_layer(@layer_data.clone, @layer_params_smooth_wx)
@tg.add_layer(@layer_data.clone, @layer_params)
file_name = '18_adv_smoothing.png'
@tg.save_to_file(file_name)
Axis are placed automatically. It would look better if axis were located on zeroes. Fortunately it is enabled by default, but check out how it would look.
Possible options:
- layer_options[:adjust_axis_to_zero] – set true to enable
@tg = TechnicalGraph.new({:adjust_axis_to_zero => false})
max = 200
@layer_data = Array.new
(0..max).each do |i|
x = -10.0 + (20.0 * i.to_f / max.to_f) + 0.11
y = 10.0 * Math.cos(i.to_f * (2.0 * 3.14 / max.to_f))
y += rand * 2.0
@layer_data << { :x => x, :y => y }
end
@tg.add_layer(@layer_data)
file_name = '19_axis_not_adjust.png'
@tg.save_to_file(file_name)