iPhones

Code

require "opal"
require "opal-d3"
require "data/iphones"

svg = D3.select("#visualization")
        .append("svg")
        .attr("height", "500px")
        .attr("width", "100%")
width = svg.style("width").to_i

x = D3.scale_linear.domain(IPhoneVariants.map(&:released).minmax).range([20,width-90]).nice
y = D3.scale_log.domain(IPhoneVariants.map(&:size).minmax).range([380,20])
c = D3.scale_ordinal.range(D3.scheme_category_20)

# If there are multiple points on same date/size combination,
# move any duplicates 15px right (or 30px for triplicates etc.)
duplicates = Hash.new(0)

graph_area = svg.append("g")
    .attr("transform", "translate(60, 20)")
graph_area.select_all("circle")
  .data(IPhoneVariants).enter
  .append("circle")
    .attr("cx"){|d|
      count = duplicates[[d.released, d.size]]
      duplicates[[d.released, d.size]] += 1
      x.(d.released) + 15 * count
    }
    .attr("cy"){|d| y.(d.size)}
    .attr("r", 10)
    .attr("fill"){|d| c.(d.name)}

axis_bottom = D3.axis_bottom(x)
  .tick_format(D3.time_format("%Y-%m-%d"))
axis_left = D3.axis_left(y)
  .tick_values([4,8,16,32,64,128,256])
  .tick_format{|v| "#{v} GB"}
graph_area.call(axis_left)
graph_area.append("g").attr("transform", "translate(0, 400)").call(axis_bottom)

D3.select("#visualization").append("table")
  .select_all("tr")
  .data(IPhones).enter
  .append("tr")
    .append("td")
    .text{|d| d.name}
    .style("background"){|d| c.(d.name)}