haskelljupyter-notebookihaskellhaskell-chart

Set Size of Plot in IHaskell (Jupyter) with Chart Package


I am able to do a simple 2D plot inside a Jupyter Notebook (with IHaskell) using the Haskell Chart Package. The code is here,

import Graphics.Rendering.Chart.Easy
cData = [1,2,3,4,3,2,1]
toRenderable  $ do
    layout_title .= "Recovered Signal"
    layout_x_axis . laxis_title .= "Time (msecs)"
    layout_y_axis . laxis_title .= "Original Input Level"
    plot (line "Amplitude" [zip [0,1..] cData])

The plot is automatically displayed in a Jupyter cell when the code cell returns a 'Renderable'

I would like to make the plot smaller.

I've tried,

I'm a mid-level Haskeller but never learned lenses which may be part of my problem. Maybe it's time to learn ..

Any help appreciated!

Tom


Solution

  • Lenses won't help you much... the problem is that IHaskell.Display.Charts hard-codes width and height. It's a bit silly...

    What you could do is write your own wrapper type for custom-sized charts, and copy the instances with the specified sizes.

    import           System.Directory
    import           Data.Default.Class
    import           Graphics.Rendering.Chart.Renderable
    import           Graphics.Rendering.Chart.Backend.Cairo
    import qualified Data.ByteString.Char8 as Char
    import           System.IO.Unsafe
    
    import           IHaskell.Display
    
    data SizedRenderable a = Sized
      { size :: (Width, Height)
      , theChart :: Renderable a
      }
    
    instance IHaskellDisplay (SizedRenderable a) where
      display renderable = do
        pngDisp <- chartData renderable PNG
    
        -- We can add `svg svgDisplay` to the output of `display`, but SVGs are not resizable in the IPython
        -- notebook.
        svgDisp <- chartData renderable SVG
    
        return $ Display [pngDisp, svgDisp]
    
    chartData :: Renderable a -> FileFormat -> IO DisplayData
    chartData (Sized size@(w,h) renderable) format = do
      switchToTmpDir
    
      -- Write the PNG image.
      let filename = ".ihaskell-chart.png"
          opts = def { _fo_format = format, _fo_size = size }
      mkFile opts filename renderable
    
      -- Convert to base64.
      imgData <- Char.readFile filename
      return $
        case format of
          PNG -> png w h $ base64 imgData
          SVG -> svg $ Char.unpack imgData
    
    
    -- TODO add signature, I can't be bothered to look it up
    mkFile opts filename renderable = renderableToFile opts filename renderable