🏠 back to Observable

Drawing arbitrary rects with Plot

Hello! In effort to better annotate my Plot charts, I’ve been attempting to figure out how to draw a rect on a chart based on explicit values. A good example of this can be found on this NYT chart (via this piece):

Something similar to the grey “Johnson & Johnson paused” block is what I’m after. I was able to get something close to this via a pair of rules, but when it comes to rendering this with the inner area filled I’ve hit a wall.

Thank you!

Maybe someone with better Plot experience can come up with a more concise solution, but here’s mine with Plot.rect: Annotating spans with Plot / Alex Garcia / Observable

Having to specify x1,y1,x2,y2 is a little tedious (it seems like Plot.cell supports giving data like [[x₀, y₀], [x₁, y₁], [x₂, y₂], …], but only for band scales), but it works in a pinch!

Another solution is to create a Plot.areaY; you just need to give it a y to specify how high it should go

Plot.plot({
  y: {
    grid: true
  },
  marks: [
    Plot.areaY(aapl, { x: "Date", y: "Close", fill: "pink" }),
    Plot.areaY([new Date(2016, 6, 1), new Date(2017, 6, 1)], {
      fill: "black",
      fillOpacity: 0.2,
      x: d => d,
      y: 180
    }),
    Plot.text([new Date(2016, 12, 1)], {
      text: () => "J&J paused",
      x: d => d,
      y: 185
    })
  ]
})

Or a barX, since the y is optional:

Plot.barX([{start: new Date(2016, 6, 1), end: new Date(2017, 6, 1)}], {x1: "start", x2: "end", fillOpacity: 0.2}) 

Ah yeah, this was what I was looking for. :raised_hands: Thank you @mbostock! (And asg017 + Fil for the other methods!)

I figured there was a way to make this happen without having to explicitly declare the max value of the y. I had been goofing around with rectX but I can see now why it didn’t work.

Is it by design that barX and barY do not work similarly to what asg017 floated via Plot.cell? I think at one point I assumed it would use the first index as x1 and second index as x2, ala:

Plot.barX([[new Date(2016, 6, 1), new Date(2017, 6, 1)]], { fillOpacity: 0.2 });

(I have no problem writing out x1 and x2, just curious. :upside_down_face:)

Yes, Plot.cell requires that both x and y are ordinal, which is not what you want here.

We could add more shorthand to Plot.barX and Plot.barY but the more shorthand we add the more ambiguities it creates, so I think it’s better to be explicit. Another way you can write the above is:

Plot.barX({length: 1}, {
  x1: [new Date(2016, 6, 1)],
  x2: [new Date(2017, 6, 1)],
  fillOpacity: 0.2
})

I think it would also be possible to make Plot.rect allow either x or y to be optional, so that you could cause Plot.rectX or Plot.rectY here instead of Plot.barX or Plot.barY; they should be the same if you’re only specifying a single quantitative dimension. I’ll file an issue for that.