gordonje
gordonje

Reputation: 51

Vega-Lite: "1 in X" custom legend labels

I'm working on a choropleth map that shows the share of the population that has confirmed positive case of Covid-19 in each political jurisdiction. Similar to this example in the per capita Mapbox graphic on this page of the The New York Times.

I figured out just about every detail expect how to customize the legend. Currently, the labels display the shareOfPop as a number. Though, I want to prefix each label with "1 in ${shareOfPop}", and to add a suffix to the final label "1 in ${shareOfPop} or more".

enter image description here.

I've created this map in an Observable Notebook.

Things I've tried so far...

Making us of the custom legend encodings

To specify label text:

vl.color()
  .fieldQ('shareOfPop')
  .scale(
    {
      scheme: "yelloworangered",
      domain: [250, 10],
      clamp: true,
    }
  )
  .legend({
    title: "Share of Pop.",
    encode: {
      labels: {text: "1 in ${datum.value}"}
    }
  })

Register a custom formatter

Which I doubt I've accomplished correctly.

Here's what my configuration looks like (which is based on the config in the Introduction to Vega-Lite notebook).

vl = {
  const [vega, vegalite, api, tooltip] = await Promise.all([
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]'
  ].map(module => require(module)));

  const options = {
    config: {
      // allow custom format types
      customFormatTypes: true,
      config: {
        view: {continuousWidth: 400, continuousHeight: 300},
        mark: {tooltip: null}
      }
    },
    init: view => {
      // initialize tooltip handler
      view.tooltip(new tooltip.Handler().call);
      // enable horizontal scrolling for large plots
      if (view.container()) view.container().style['overflow-x'] = 'auto';
      // register a custom expression function...am I doing this right???       
      vega.expressionFunction('1inX', function(datum) {
        return `1 in ${datum}`
      })
    },
    view: {
      // view constructor options
      loader: vega.loader({baseURL: 'https://cdn.jsdelivr.net/npm/vega-datasets@1/'}),
      renderer: 'canvas'
    }
  };
  
  return api.register(vega, vegalite, options);
}

Then I specify this custom formatType when defining the mark:

vl.color()
  .fieldQ('shareOfPop')
  .scale(
    {
      scheme: "yelloworangered",
      domain: [250, 10],
      clamp: true,
    }
  )
  .legend({
    title: "Share of Pop.",
    formatType: "1inX",
  })
)

Neither of these approaches produced any noticeable change.

Upvotes: 1

Views: 727

Answers (1)

gordonje
gordonje

Reputation: 51

Gonna answer my own question here.

Turns out Legend has a general labelExpr property that allows you to specify a Vega expression for customizing the label.

In my case, I wanted to always prepend the string "1 in ", and also append "+" when over may domain limit. Here's how I did it using the join() and if() functions.

...
vl.color()
  .legend(
    {
      labelExpr: "join(['1 in ', datum.value, if(datum.value >= 250, '+', '')], '')"
    }
  )

This property isn't documented for Legend, though it is for for Axis).

Upvotes: 4

Related Questions