Menu

Multiline (html labels + quadtree tooltip)Edit

A multiline example with a quadtree tooltip. This is an interesting example because the data exists in a few different structures:

  1. We're loading data from a "wide" format CSV file where each series has its own column name.

    [
      {
        month: 2015-03-31T22:00:00.000Z,
        apples: '3840',
        bananas: '1920',
        cherries: '960',
        dates: '400'
      },
      {
        month: 2015-02-28T23:00:00.000Z,
        apples: '1600',
        bananas: '1440',
        cherries: '960',
        dates: '400'
      },
      ...
    

    We need to first turn this into...

  2. ...a "long" format, where each type of fruit is grouped into its own array and each datapoint is a row. The column name becomes a property on the group whose name we define with the zKey variable.

    [
        {
            "fruit": "apples",
            "values": [
                {
                    "value": 3840,
                    "month": "2015-03-31T22:00:00.000Z",
                    "fruit": "apples"
                },
                {
                    "value": 1600,
                    "month": "2015-02-28T23:00:00.000Z",
                    "fruit": "apples"
                },
                ...
            ]
        },
        {
            "fruit": "bananas",
            "values": [
                {
                    "value": 1920,
                    "month": "2015-03-31T22:00:00.000Z",
                    "fruit": "bananas"
                },
        ...
    
  3. We also need a flat, ungrouped array of objects so that Layer Cake can measure the full data extents. This gets passed to the flatData prop so the scales know the full domain of the data.

    [
      { value: 3840, month: 2015-03-31T22:00:00.000Z, fruit: 'apples' },
      { value: 1600, month: 2015-02-28T23:00:00.000Z, fruit: 'apples' },
      { value: 640, month: 2015-01-31T23:00:00.000Z, fruit: 'apples' },
      { value: 320, month: 2014-12-31T23:00:00.000Z, fruit: 'apples' },
      { value: 1920, month: 2015-03-31T22:00:00.000Z, fruit: 'bananas' },
      ...
    

We're using Layer Cake's groupLonger transform function to do steps one and two. See the server-side rendered example for a regular JavaScript transform.

<script>
  import { LayerCake, Svg, Html, groupLonger, flatten } from 'layercake';

  import { scaleOrdinal } from 'd3-scale';
  import { timeParse, timeFormat } from 'd3-time-format';
  import { format } from 'd3-format';

  import MultiLine from './_components/MultiLine.svelte';
  import AxisX from './_components/AxisX.svelte';
  import AxisY from './_components/AxisY.svelte';
  import Labels from './_components/GroupLabels.html.svelte';
  import SharedTooltip from './_components/SharedTooltip.html.svelte';

  // This example loads csv data as json using @rollup/plugin-dsv
  import data from './_data/fruit.csv';

  /* --------------------------------------------
   * Set what is our x key to separate it from the other series
   */
  const xKey = 'month';
  const yKey = 'value';
  const zKey = 'fruit';

  const xKeyCast = timeParse('%Y-%m-%d');

  const seriesNames = Object.keys(data[0]).filter(d => d !== xKey);
  const seriesColors = ['#ffe4b8', '#ffb3c0', '#ff7ac7', '#ff00cc'];

  /* --------------------------------------------
   * Cast values
   */
  data.forEach(d => {
    d[xKey] = typeof d[xKey] === 'string' ? xKeyCast(d[xKey]) : d[xKey];

    seriesNames.forEach(name => {
      d[name] = +d[name];
    });
  });

  const formatLabelX = timeFormat('%b. %e');
  const formatLabelY = d => format(`~s`)(d);

  const groupedData = groupLonger(data, seriesNames, {
    groupTo: zKey,
    valueTo: yKey
  });
</script>

<div class="chart-container">
  <LayerCake
    padding={{ top: 7, right: 10, bottom: 20, left: 25 }}
    x={xKey}
    y={yKey}
    z={zKey}
    yDomain={[0, null]}
    zScale={scaleOrdinal()}
    zRange={seriesColors}
    flatData={flatten(groupedData, 'values')}
    data={groupedData}
  >
    <Svg>
      <AxisX
        gridlines={false}
        ticks={data.map(d => d[xKey]).sort((a, b) => a - b)}
        format={formatLabelX}
        snapLabels
        tickMarks
      />
      <AxisY ticks={4} format={formatLabelY} />
      <MultiLine />
    </Svg>

    <Html>
      <Labels />
      <SharedTooltip formatTitle={formatLabelX} dataset={data} />
    </Html>
  </LayerCake>
</div>

<style>
  /*
    The wrapper div needs to have an explicit width and height in CSS.
    It can also be a flexbox child or CSS grid element.
    The point being it needs dimensions since the <LayerCake> element will
    expand to fill it.
  */
  .chart-container {
    width: 100%;
    height: 250px;
  }
</style>