Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secondary axis support for line charts #431

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/basic_charts.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,36 @@
end
end

# secondary axis in line chart
wb.add_worksheet(:name => "Secondary axis") do |sheet|
sheet.add_row %w(first second)
10.times do
sheet.add_row [ rand(24)+1, rand(24)*100+1]
end
sheet.add_chart(Axlsx::LineChart, :title => "Simple Line Chart", :rotX => 30, :rotY => 20) do |chart|
chart.start_at 0, 5
chart.end_at 10, 25
chart.add_series :data => sheet["A2:A11"], :title => sheet["A1"], :color => "5B9BD5", :show_marker => true, :smooth => true
chart.add_series :data => sheet["B2:B11"], :title => sheet["B1"], :color => "ED7D31", :on_primary_axis => false

chart.catAxis.title = 'X Axis'
chart.valAxis.title = 'Primary Axis'
chart.secValAxis.title = "Secondary Axis"

# Set the text color of the title
chart.catAxis.title.text_color = "404040"
chart.valAxis.title.text_color = "5B9BD5"
chart.secValAxis.title.text_color = "ED7D31"

# Set the color of the axis values
chart.valAxis.text_color = "5B9BD5"
chart.secValAxis.text_color = "ED7D31"

# Set the luminance
chart.catAxis.gridlines_luminance = 0.25
chart.valAxis.gridlines_luminance = 0.25
end
end

p.serialize('basic_charts.xlsx')

5 changes: 3 additions & 2 deletions lib/axlsx/drawing/axes.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ def initialize(options={})
end
end

# [] provides assiciative access to a specic axis store in an axes
# [] provides associative access to a specic axis store in an axes
# instance.
# @return [Axis]
def [](name)
axes.assoc(name)[1]
a = axes.assoc(name)
if a.nil? then nil else a[1] end
end

# Serializes the object
Expand Down
43 changes: 42 additions & 1 deletion lib/axlsx/drawing/axis.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,21 @@ def initialize(options={})
# @return [Boolean]
attr_reader :gridlines

# specifies the gradient of the gridlines
# @return [Float]
attr_reader :gridlines_luminance

# specifies if gridlines should be shown in the chart
# @return [Boolean]
attr_reader :delete

# the title for the axis. This can be a cell or a fixed string.
attr_reader :title

# Text color property
# @return [String]
attr_reader :text_color

# The color for this axis. This value is used when rendering the axis line in the chart.
# colors should be in 6 character rbg format
# @return [String] the rbg color assinged.
Expand Down Expand Up @@ -116,6 +124,14 @@ def format_code=(v) Axlsx::validate_string(v); @format_code = v; end
# default true
def gridlines=(v) Axlsx::validate_boolean(v); @gridlines = v; end

# Specify the gridlines luminance
# must be a float in (0..1)
# default nil
def gridlines_luminance=(v)
DataTypeValidator.validate 'Axis.gridlines_luminance', Float, v
@gridlines_luminance = v
end

# Specify if axis should be removed from the chart
# default false
def delete=(v) Axlsx::validate_boolean(v); @delete = v; end
Expand Down Expand Up @@ -146,6 +162,13 @@ def title=(v)
end
end

# Assigns a text color to the title
# colors should be in 6 character rbg format
def text_color=(v)
DataTypeValidator.validate 'Axis.text_color', String, v
@text_color = v
end

# Serializes the object
# @param [String] str
# @return [String]
Expand All @@ -157,11 +180,23 @@ def to_xml_string(str = '')
str << '<c:majorGridlines>'
# TODO shape properties need to be extracted into a class
if gridlines == false
# No gridlines
str << '<c:spPr>'
str << '<a:ln>'
str << '<a:noFill/>'
str << '</a:ln>'
str << '</c:spPr>'
elsif !@gridlines_luminance.nil? then
# light gridlines
str << '<c:spPr>'
str << '<a:ln>'
str << '<a:solidFill>'
str << '<a:schemeClr val="bg2">'
str << ('<a:lumMod val="' << (@gridlines_luminance*100000).to_i.to_s << '"/>')
str << '</a:schemeClr>'
str << '</a:solidFill>'
str << '</a:ln>'
str << '</c:spPr>'
end
str << '</c:majorGridlines>'
@title.to_xml_string(str) unless @title == nil
Expand All @@ -180,7 +215,13 @@ def to_xml_string(str = '')
str << '</a:solidFill></a:ln></c:spPr>'
end
# some potential value in implementing this in full. Very detailed!
str << ('<c:txPr><a:bodyPr rot="' << @label_rotation.to_s << '"/><a:lstStyle/><a:p><a:pPr><a:defRPr/></a:pPr><a:endParaRPr/></a:p></c:txPr>')
str << ('<c:txPr><a:bodyPr rot="' << @label_rotation.to_s << '"/><a:lstStyle/><a:p><a:pPr>')
if @text_color.nil? then
str << '<a:defRPr/>'
else
str << ('<a:defRPr><a:solidFill><a:srgbClr val="' << @text_color.to_s << '"/></a:solidFill></a:defRPr>')
end
str << '</a:pPr><a:endParaRPr/></a:p></c:txPr>'
str << ('<c:crossAx val="' << @cross_axis.id.to_s << '"/>')
str << ('<c:crosses val="' << @crosses.to_s << '"/>')
end
Expand Down
12 changes: 12 additions & 0 deletions lib/axlsx/drawing/chart.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def initialize(frame, options={})
@show_legend = true
@legend_position = :r
@display_blanks_as = :gap
@rounded_corners = true
@series_type = Series
@title = Title.new
@bg_color = nil
Expand Down Expand Up @@ -98,6 +99,11 @@ def vary_colors=(v) Axlsx::validate_boolean(v); @vary_colors = v; end
# @return [String]
attr_reader :bg_color

# Use rounded corners for the chart?
# It defaults to true
# @return [Boolean]
attr_reader :rounded_corners

# The relationship object for this chart.
# @return [Relationship]
def relationship
Expand Down Expand Up @@ -178,13 +184,19 @@ def bg_color=(v)
@bg_color = v
end

# Should the chart have rounded corners?
# @param [Boolean] v
# @return [Boolean]
def rounded_corners=(v) Axlsx::validate_boolean(v); @rounded_corners = v; end

# Serializes the object
# @param [String] str
# @return [String]
def to_xml_string(str = '')
str << '<?xml version="1.0" encoding="UTF-8"?>'
str << ('<c:chartSpace xmlns:c="' << XML_NS_C << '" xmlns:a="' << XML_NS_A << '" xmlns:r="' << XML_NS_R << '">')
str << ('<c:date1904 val="' << Axlsx::Workbook.date1904.to_s << '"/>')
str << '<c:roundedCorners val="0"/>' if !@rounded_corners
str << ('<c:style val="' << style.to_s << '"/>')
str << '<c:chart>'
@title.to_xml_string str
Expand Down
98 changes: 84 additions & 14 deletions lib/axlsx/drawing/line_chart.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,28 @@ def cat_axis
end
alias :catAxis :cat_axis

# the category axis
# the values axis
# @return [ValAxis]
def val_axis
axes[:val_axis]
end
alias :valAxis :val_axis

# must be one of [:percentStacked, :clustered, :standard, :stacked]
# the secondary category axis
# @return [sec_cat_axis]
def sec_cat_axis
axes[:sec_cat_axis]
end
alias :secCatAxis :sec_cat_axis

# the secondary values axis
# @return [sec_val_axis]
def sec_val_axis
axes[:sec_val_axis]
end
alias :secValAxis :sec_val_axis

# must be one of [:percentStacked, :clustered, :standard, :stacked]
# @return [Symbol]
attr_reader :grouping

Expand Down Expand Up @@ -76,24 +90,80 @@ def node_name
# @param [String] str
# @return [String]
def to_xml_string(str = '')
super(str) do
str << ("<c:" << node_name << ">")
str << ('<c:grouping val="' << grouping.to_s << '"/>')
str << ('<c:varyColors val="' << vary_colors.to_s << '"/>')
@series.each { |ser| ser.to_xml_string(str) }
@d_lbls.to_xml_string(str) if @d_lbls
yield if block_given?
axes.to_xml_string(str, :ids => true)
str << ("</c:" << node_name << ">")
axes.to_xml_string(str)
if @series.all? {|s| s.on_primary_axis} then
# Only a primary val axis
super(str) do
str << ("<c:" << node_name << ">")
str << ('<c:grouping val="' << grouping.to_s << '"/>')
str << ('<c:varyColors val="' << vary_colors.to_s << '"/>')
@series.each { |ser| ser.to_xml_string(str) }
@d_lbls.to_xml_string(str) if @d_lbls
yield if block_given?
axes.to_xml_string(str, :ids => true)
str << ("</c:" << node_name << ">")
axes.to_xml_string(str)
end
else
# Two value axes
super(str) do
# First axis
str << ("<c:" << node_name << ">")
str << ('<c:grouping val="' << grouping.to_s << '"/>')
str << ('<c:varyColors val="' << vary_colors.to_s << '"/>')
@series.select {|s| s.on_primary_axis}.each { |s| s.to_xml_string(str) }
@d_lbls.to_xml_string(str) if @d_lbls
yield if block_given?
str << ('<c:axId val="' << axes[:cat_axis].id.to_s << '"/>')
str << ('<c:axId val="' << axes[:val_axis].id.to_s << '"/>')
str << ("</c:" << node_name << ">")

# Secondary axis
str << ("<c:" << node_name << ">")
str << ('<c:grouping val="' << grouping.to_s << '"/>')
str << ('<c:varyColors val="' << vary_colors.to_s << '"/>')
@series.select {|s| !s.on_primary_axis}.each { |s| s.to_xml_string(str) }
@d_lbls.to_xml_string(str) if @d_lbls
yield if block_given?
str << ('<c:axId val="' << axes[:sec_cat_axis].id.to_s << '"/>')
str << ('<c:axId val="' << axes[:sec_val_axis].id.to_s << '"/>')
str << ("</c:" << node_name << ">")

# The axes
axes.to_xml_string(str)
end
end
end

# The axes for this chart. LineCharts have a category and value
# axis.
# axis. If any series is on the secondary axis we will have two
# category and two value axes.
# @return [Axes]
def axes
@axes ||= Axes.new(:cat_axis => CatAxis, :val_axis => ValAxis)
if @axes.nil? then
# add the normal axes
@axes = Axes.new(:cat_axis => CatAxis, :val_axis => ValAxis)

# add the secondary axes if needed
if @series.any? {|s| !s.on_primary_axis} then
if @axes[:sec_cat_axis].nil? then
@axes.add_axis(:sec_cat_axis, Axlsx::CatAxis)
sec_cat_axis = @axes[:sec_cat_axis]
sec_cat_axis.ax_pos = :b
sec_cat_axis.delete = 1
sec_cat_axis.gridlines = false
end
if @axes[:sec_val_axis].nil? then
@axes.add_axis(:sec_val_axis, Axlsx::ValAxis)
sec_val_axis = @axes[:sec_val_axis]
sec_val_axis.ax_pos = :r
sec_val_axis.gridlines = false
sec_val_axis.crosses = :max
end
end
end

# return
@axes
end
end
end
11 changes: 11 additions & 0 deletions lib/axlsx/drawing/line_series.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class LineSeries < Series
# @return [Boolean]
attr_reader :smooth

# on primary or secondary axis
# @return [Boolean]
attr_reader :on_primary_axis

# Creates a new series
# @option options [Array, SimpleTypedList] data
# @option options [Array, SimpleTypedList] labels
Expand All @@ -39,6 +43,7 @@ def initialize(chart, options={})
@show_marker = false
@marker_symbol = options[:marker_symbol] ? options[:marker_symbol] : :default
@smooth = false
@on_primary_axis = true
@labels, @data = nil, nil
super(chart, options)
@labels = AxDataSource.new(:data => options[:labels]) unless options[:labels].nil?
Expand Down Expand Up @@ -68,6 +73,12 @@ def smooth=(v)
@smooth = v
end

# is it on the primary axis?
def on_primary_axis=(v)
Axlsx::validate_boolean(v)
@on_primary_axis = v
end

# Serializes the object
# @param [String] str
# @return [String]
Expand Down
19 changes: 19 additions & 0 deletions lib/axlsx/drawing/title.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ class Title
# @return [String]
attr_reader :text_size

# Text color property
# @return [String]
attr_reader :text_color

# The cell that holds the text for the title. Setting this property will automatically update the text attribute.
# @return [Cell]
attr_reader :cell
Expand Down Expand Up @@ -51,6 +55,13 @@ def cell=(v)
v
end

# Assigns a text color to the title
# colors should be in 6 character rbg format
def text_color=(v)
DataTypeValidator.validate 'Title.text_color', String, v
@text_color = v
end

# Not implemented at this time.
#def layout=(v) DataTypeValidator.validate 'Title.layout', Layout, v; @layout = v; end
#def overlay=(v) Axlsx::validate_boolean v; @overlay=v; end
Expand Down Expand Up @@ -79,7 +90,15 @@ def to_xml_string(str = '')
str << '<a:lstStyle/>'
str << '<a:p>'
str << '<a:r>'
if @text_color.nil? then
str << ('<a:rPr sz="' << @text_size.to_s << '"/>')
else
str << ('<a:rPr sz="' << @text_size.to_s << '">')
str << '<a:solidFill>'
str << ('<a:srgbClr val="' << @text_color.to_s << '"/>')
str << '</a:solidFill>'
str << '</a:rPr>'
end
str << ('<a:t>' << @text.to_s << '</a:t>')
str << '</a:r>'
str << '</a:p>'
Expand Down