d3-scale#scaleBand JavaScript Examples

The following examples show how to use d3-scale#scaleBand. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: horizontal-bar-chart.js    From website with Apache License 2.0 5 votes vote down vote up
HorizontalBarChart = ({
  data,
  fill,
  height,
  marginBottom = 0,
  marginLeft = 0,
  marginRight = 0,
  marginTop = 0,
  xTicks,
  width,
  xMax = null,
}) => {
  const totalXMargin = marginLeft + marginRight
  const totalYMargin = marginTop + marginBottom
  const yScale = scaleBand()
    .domain(data.map(d => d.name))
    .range([0, height - totalYMargin])
    .padding(0.2)
  const formatTick = format('~s')
  const xScale = scaleLinear()
    .domain([120, xMax || max(data, d => d.value)])
    .nice()
    .range([0, width - totalXMargin])

  return (
    <svg
      className={chartStyles.chart}
      viewBox={`0 0 ${width} ${height}`}
      aria-hidden
    >
      <g transform={`translate(${marginLeft} ${marginTop})`}>
        {xScale.ticks(xTicks).map(tick => (
          <g key={tick}>
            <text
              className={`${chartStyles.label} ${chartStyles.xTickLabel}`}
              x={xScale(tick)}
              y={height - marginBottom}
            >
              {formatTick(tick)}
            </text>
            <line
              className={chartStyles.gridLine}
              x1={xScale(tick)}
              x2={xScale(tick)}
              y1={0}
              y2={height - totalYMargin}
            />
          </g>
        ))}
      </g>

      <g transform={`translate(0, ${marginTop})`}>
        {data.map(d => (
          /* Do not remove nested svg. See https://github.com/COVID19Tracking/website/pull/645#discussion_r411676987 */
          <svg
            y={yScale(d.name) + 20}
            x={marginLeft - 10}
            className={chartStyles.yTickLabel}
            key={d.name}
          >
            <text className={chartStyles.label}>{`${d.name}`}</text>
          </svg>
        ))}
      </g>

      <g transform={`translate(${marginLeft}, ${marginTop})`}>
        {data.map(d => (
          <rect
            key={d.name}
            x={0}
            y={yScale(d.name)}
            height={yScale.bandwidth()}
            width={xScale(d.value)}
            fill={fill}
          />
        ))}
      </g>
    </svg>
  )
}
Example #2
Source File: node-networks-overlay.js    From ThreatMapper with Apache License 2.0 5 votes vote down vote up
x = scaleBand()
Example #3
Source File: bar-chart.js    From website with Apache License 2.0 4 votes vote down vote up
BarChart = ({
  data,
  lineData,
  refLineData,
  annotations,
  handleAnnotationClick,
  fill,
  lineColor,
  marginBottom,
  marginLeft,
  marginRight,
  marginTop,
  showTicks,
  width,
  height,
  yMax,
  yTicks,
  lastXTick,
  renderTooltipContents,
  perCapLabel,
}) => {
  const chartRef = useRef()
  const [tooltip, setTooltip] = useState(null)
  // Used for tooltip optimization
  const [timeoutRef, setTimeoutRef] = useState(null)
  const [keyboardFocus, setKeyboardFocus] = useState(false)

  // Used when placing annotations
  const getValueForDate = date => {
    const dateData = data.find(d => d.date.getTime() === date.getTime())
    return dateData && dateData.value
  }
  const totalXMargin = marginLeft + marginRight
  const totalYMargin = marginTop + marginBottom
  // The x range is over the area in which the chart should be displaying.
  // We don't use an X transform to place it in the correct spot, we use range
  // instead
  const xScale = scaleBand()
    .domain(data.map(d => d.date))
    .range([marginLeft, width - marginRight])
    .padding(0.1)
  const dateDomain = extent(data, d => d.date)
  // Should probably refactor to use a single x-axis scale
  // but the bars make use of the band.
  const xScaleTime = scaleTime()
    .domain(dateDomain)
    .range([marginLeft, width - marginRight])

  const yMaxEffective =
    yMax || max([...data, ...(refLineData || [])], d => d.value)

  const yScale = scaleLinear()
    .domain([0, yMaxEffective])
    .nice()
    .range([height - totalYMargin, 0])

  const msInOneMonth = 2628000000
  const monthlyTickInterval = Math.ceil(
    Math.abs((dateDomain[1] - dateDomain[0]) / (msInOneMonth * 6)),
  )

  const xTickAmount = timeMonth.every(monthlyTickInterval)

  const yTicksThreshold = 4
  const yTicksEffective =
    yTicks || yMaxEffective < yTicksThreshold ? yMaxEffective : yTicksThreshold

  const lastTime = xScaleTime.ticks(timeDay.every(1)).pop()

  let lineFn = null
  if (lineData) {
    lineFn = line()
      .defined(d => !Number.isNaN(d.value) && d.value !== null)
      .curve(curveCardinal)
      .x(d => xScaleTime(d.date))
      .y(d => yScale(d.value))
  }
  const hover = (event, d) => {
    // Ensure that tooltip doesn't flash when transitioning between bars
    if (timeoutRef) {
      clearTimeout(timeoutRef)
    }
    const isTouchEvent = !event.clientX
    const eventX = isTouchEvent ? event.touches[0].clientX : event.clientX
    const eventY = isTouchEvent ? event.touches[0].clientY : event.clientY
    setTooltip({
      top: isTouchEvent ? eventY - 130 : eventY + 10,
      left: isTouchEvent ? eventX - 80 : eventX + 5,
      d,
    })
  }

  const mouseOut = () => {
    if (timeoutRef) {
      clearTimeout(timeoutRef)
    }
    setTimeoutRef(setTimeout(() => setTooltip(null), 200))
  }

  useEffect(() => {
    if (keyboardFocus === false || typeof data[keyboardFocus] === 'undefined') {
      return
    }
    const column = data[keyboardFocus]
    setTooltip({
      top: chartRef.current.getBoundingClientRect().top,
      left: chartRef.current.getBoundingClientRect().left,
      d: column,
    })
  }, [keyboardFocus])

  return (
    <>
      <svg
        className={classnames(chartStyles.chart, styles.chart)}
        viewBox={`0 0 ${width} ${height}`}
        tabIndex="0"
        aria-hidden
        ref={chartRef}
        onBlur={() => {
          setTooltip(null)
          setKeyboardFocus(false)
        }}
        onKeyDown={event => {
          if (event.key === 'Escape') {
            setTooltip(null)
            setKeyboardFocus(false)
            chartRef.current.blur()
          }
          if (event.key === 'ArrowRight') {
            setKeyboardFocus(
              keyboardFocus < data.length ? keyboardFocus + 1 : data.length,
            )
          }
          if (
            (event.shiftKey && event.key === 'Tab') ||
            event.key === 'ArrowLeft'
          ) {
            setKeyboardFocus(keyboardFocus > 0 ? keyboardFocus - 1 : 0)
          }
        }}
      >
        <g transform={`translate(${marginLeft} ${marginTop})`}>
          <text className={classnames(chartStyles.label, styles.directions)}>
            Use arrows to move, Escape to leave.
          </text>
        </g>
        {/* y ticks */}
        <g transform={`translate(${marginLeft} ${marginTop})`}>
          {yScale.ticks(yTicksEffective).map(
            (tick, i) =>
              i < showTicks && (
                <g key={tick}>
                  {/* Do not remove nested svg. See https://github.com/COVID19Tracking/website/pull/645#discussion_r411676987 */}
                  <svg
                    y={yScale(tick) + 6}
                    x="-10"
                    className={chartStyles.yTickLabel}
                  >
                    <text className={chartStyles.label}>
                      {formatNumber(tick)}
                      {tick > 0 &&
                        perCapLabel /* this only displays if passed */}
                    </text>
                  </svg>
                  <line
                    className={chartStyles.gridLine}
                    x1={0}
                    x2={width - totalXMargin}
                    y1={yScale(tick)}
                    y2={yScale(tick)}
                  />
                </g>
              ),
          )}
        </g>
        {/* x ticks (dates) */}
        <g transform={`translate(0, ${height - marginBottom})`}>
          {xScaleTime.ticks(xTickAmount).map(d => (
            <Fragment key={`x-${d}`}>
              <text
                className={`${chartStyles.label} ${chartStyles.xTickLabel}`}
                key={d}
                x={xScaleTime(d)}
                y="20"
              >{`${formatDate(d)}`}</text>
              <line
                className={chartStyles.label}
                stroke={colors.colorSlate500}
                x1={xScaleTime(d)}
                y1="0"
                x2={xScaleTime(d)}
                y2="5"
              />
            </Fragment>
          ))}
          {lastXTick && (
            <>
              <text
                className={`${chartStyles.label} ${chartStyles.xTickLabel}`}
                x={xScaleTime(lastTime)}
                y="20"
              >{`${formatDate(lastTime)}`}</text>
              <line
                className={chartStyles.label}
                stroke={colors.colorSlate500}
                x1={xScaleTime(lastTime)}
                y1="0"
                x2={xScaleTime(lastTime)}
                y2="5"
              />
            </>
          )}
        </g>

        <mask id="dataMask">
          <rect
            x="0"
            y="0"
            width={width - marginRight}
            height={height - totalYMargin}
            fill="white"
          />
        </mask>

        {/* data */}
        <g transform={`translate(0 ${marginTop})`} mask="url(#dataMask)">
          {/* bars (data) */}
          {data.map((d, key) => (
            <rect
              key={d.date + d.value}
              x={xScale(d.date)}
              y={yScale(d.value)}
              height={yScale(0) - yScale(d.value)}
              width={xScale.bandwidth()}
              fillOpacity={lineData ? 1 : 0.8}
              fill={fill}
              className={classnames(
                renderTooltipContents && styles.interactiveBar,
                key === keyboardFocus && styles.selected,
              )}
              onMouseOver={event => hover(event, d)}
              onFocus={event => hover(event, d)}
              onMouseOut={mouseOut}
              onBlur={mouseOut}
            />
          ))}
          {/* line */}
          {lineData && (
            <path
              d={lineFn(lineData)}
              stroke={lineColor}
              strokeWidth="3"
              fill="none"
            />
          )}
          {/* reference line */}
          {refLineData && (
            <path
              d={lineFn(refLineData)}
              stroke="black"
              strokeWidth="2"
              strokeDasharray="4"
              fill="none"
            />
          )}
        </g>
        {/* annotations */}
        {annotations && (
          <g transform={`translate(0 ${marginTop})`}>
            {annotations
              .filter(
                annotation =>
                  xScaleTime(annotation.date) >= xScaleTime(dateDomain[0]) &&
                  xScaleTime(annotation.date) <= xScaleTime(dateDomain[1]),
              )
              .map(d => (
                <AnnotationBubble
                  content={d}
                  xScaleTime={xScaleTime}
                  yScale={yScale}
                  handleAnnotationClick={handleAnnotationClick}
                  getValueForDate={getValueForDate}
                />
              ))}
          </g>
        )}
      </svg>
      {renderTooltipContents && tooltip && (
        <Tooltip {...tooltip}>{renderTooltipContents(tooltip.d)} </Tooltip>
      )}
    </>
  )
}
Example #4
Source File: county-chart.js    From website with Apache License 2.0 4 votes vote down vote up
CountyChart = ({ data, field, label, increments }) => {
  const height = 400
  const width = 400
  const labelOffset = 150
  const heightOffset = 50

  const yScale = scaleBand()
    .domain(data.map((d, index) => index))
    .range([0, height])

  const xScale = scaleLinear()
    .domain([0, max(data, d => d[field])])
    .nice()
    .range([0, width])

  const verticalLines = []
  for (let i = 0; i < max(data, d => d[field]); i += increments) {
    verticalLines.push(i)
  }

  return (
    <svg
      className={countyChartStyles.chart}
      viewBox={`0 0 ${width + labelOffset} ${height + heightOffset}`}
    >
      <g
        transform={`translate(${labelOffset} ${heightOffset})`}
        height={height - heightOffset}
        width={width - labelOffset}
      >
        {data.map((d, index) => (
          <rect
            key={`${field}-${d.name}-${d.state}`}
            x={0}
            y={yScale(index)}
            height={10}
            width={xScale(d[field])}
            className={`${countyChartStyles.bar} ${
              countyChartStyles[groupClasses[d.demographics.largestRace1]]
            }`}
            fill="#000"
          />
        ))}
      </g>
      {verticalLines.map(tick => (
        <g key={`${field}-${tick}`}>
          <svg y={20} x={xScale(tick) + labelOffset} width={1}>
            <line
              x1="0"
              y1="0"
              x2="0"
              y2={height + heightOffset}
              className={countyChartStyles.verticalTick}
              style={{ height }}
            />
          </svg>
        </g>
      ))}

      <g transform="translate(0, 15)">
        <svg
          y={0}
          x={labelOffset}
          width={labelOffset}
          className={countyChartStyles.tick}
        >
          <text className={countyChartStyles.label}>{label}</text>
        </svg>
        {xScale.ticks(3).map(
          (tick, i) =>
            i < 3 && (
              <g key={`${field}-${tick}`}>
                <svg
                  y={20}
                  x={xScale(tick) + labelOffset}
                  width={labelOffset}
                  className={countyChartStyles.tick}
                >
                  <text
                    className={classnames(
                      countyChartStyles.label,
                      countyChartStyles.centered,
                    )}
                  >
                    {tick.toLocaleString()}
                  </text>
                </svg>
              </g>
            ),
        )}
      </g>
      {data.map((d, index) => (
        <g
          key={`${d.field}-${d.county}`}
          transform={`translate(0, ${heightOffset})`}
        >
          <svg
            y={yScale(index) + 10}
            x={labelOffset - 10}
            width={labelOffset}
            className={countyChartStyles.countyLabel}
          >
            <text className={countyChartStyles.label}>
              {d.name}, {d.demographics.abbreviation}
            </text>
          </svg>
        </g>
      ))}
    </svg>
  )
}
Example #5
Source File: DeltaBarGraph.js    From covid19india-react with MIT License 4 votes vote down vote up
function DeltaBarGraph({timeseries, statistic, lookback}) {
  const svgRef = useRef();
  const [wrapperRef, {width, height}] = useMeasure();

  const pastDates = Object.keys(timeseries || {}).filter(
    (date) => date <= getIndiaDateYesterdayISO()
  );
  const dates = pastDates.slice(-lookback);

  const getDeltaStatistic = useCallback(
    (date, statistic) => {
      return getStatistic(timeseries?.[date], 'delta', statistic);
    },
    [timeseries]
  );

  useEffect(() => {
    if (!width) return;
    const svg = select(svgRef.current);

    const chartRight = width - margin.right;
    const chartBottom = height - margin.bottom;
    const r = 5;

    // const formatTime = timeFormat('%e %b');
    const xScale = scaleBand()
      .domain(dates)
      .range([margin.left, chartRight])
      .paddingInner(width / 1000);

    const [statisticMin, statisticMax] = extent(dates, (date) =>
      getDeltaStatistic(date, statistic)
    );

    const yScale = scaleLinear()
      .domain([Math.min(0, statisticMin || 0), Math.max(1, statisticMax || 0)])
      .range([chartBottom, margin.top]);

    const xAxis = axisBottom(xScale)
      .tickSize(0)
      .tickFormat((date) => formatDate(date, 'dd MMM'));

    const t = svg.transition().duration(D3_TRANSITION_DURATION);

    const statisticConfig = STATISTIC_CONFIGS[statistic];

    svg
      .select('.x-axis')
      .transition(t)
      .style('transform', `translate3d(0, ${yScale(0)}px, 0)`)
      .call(xAxis)
      .on('start', () => svg.select('.domain').remove())
      .selectAll('text')
      .attr('y', 0)
      .attr('dy', (date, i) =>
        getDeltaStatistic(date, statistic) < 0 ? '-1em' : '1.5em'
      )
      .style('text-anchor', 'middle')
      .attr('fill', statisticConfig.color);

    svg
      .selectAll('.bar')
      .data(dates)
      .join((enter) =>
        enter
          .append('path')
          .attr('class', 'bar')
          .attr('d', (date) =>
            roundedBar(xScale(date), yScale(0), xScale.bandwidth(), 0, r)
          )
      )
      .transition(t)
      .attr('d', (date) =>
        roundedBar(
          xScale(date),
          yScale(0),
          xScale.bandwidth(),
          yScale(0) - yScale(getDeltaStatistic(date, statistic)),
          r
        )
      )
      .attr('fill', (date, i) => {
        return i < dates.length - 1
          ? statisticConfig.color + '90'
          : statisticConfig.color;
      });

    const textSelection = svg
      .selectAll('.label')
      .data(dates)
      .join('text')
      .attr('class', 'label')
      .attr('x', (date) => xScale(date) + xScale.bandwidth() / 2)
      .text((date) =>
        formatNumber(
          getDeltaStatistic(date, statistic),
          statisticConfig?.showDelta || statisticConfig?.nonLinear
            ? statisticConfig.format
            : 'short'
        )
      );

    textSelection
      .transition(t)
      .attr('fill', statisticConfig.color)
      .attr('y', (date) => {
        const val = getDeltaStatistic(date, statistic);
        return yScale(val) + (val < 0 ? 15 : -6);
      });

    textSelection
      .append('tspan')
      .attr(
        'dy',
        (date) => `${getDeltaStatistic(date, statistic) < 0 ? 1.2 : -1.2}em`
      )
      .attr('x', (date) => xScale(date) + xScale.bandwidth() / 2)
      .text((date, i) => {
        if (i === 0) return '';
        const prevVal = getDeltaStatistic(dates[i - 1], statistic);
        if (!prevVal) return '';
        const delta = getDeltaStatistic(date, statistic) - prevVal;
        return `${delta > 0 ? '+' : ''}${formatNumber(
          (100 * delta) / Math.abs(prevVal),
          '%'
        )}`;
      })
      .transition(t)
      .attr('fill', statisticConfig.color + '90');
  }, [dates, height, statistic, width, getDeltaStatistic]);

  return (
    <div className="DeltaBarGraph" ref={wrapperRef}>
      <svg
        ref={svgRef}
        width={width}
        height={250}
        viewBox={`0 0 ${width} ${height}`}
        preserveAspectRatio="xMidYMid meet"
      >
        <g className="x-axis" />
        <g className="y-axis" />
      </svg>
    </div>
  );
}
Example #6
Source File: MapLegend.js    From covid19india-react with MIT License 4 votes vote down vote up
function legend({
  svg,
  color,
  title,
  tickSize = 6,
  width = 320,
  height = 44 + tickSize,
  marginTop = 18,
  marginRight = 0,
  marginBottom = 16 + tickSize,
  marginLeft = 0,
  ticks = width / 64,
  tickFormat,
  tickValues,
  ordinalWeights,
} = {}) {
  const t = svg.transition().duration(D3_TRANSITION_DURATION);

  let tickAdjust = (g) => {
    const ticks = g.selectAll('.tick line');
    ticks.attr('y1', marginTop + marginBottom - height);
    // select(ticks.nodes()[ticks.size() - 1]).remove();
  };
  let x;

  // Continuous
  if (color.interpolate) {
    const n = Math.min(color.domain().length, color.range().length);

    x = color
      .copy()
      .rangeRound(quantize(interpolate(marginLeft, width - marginRight), n));

    svg
      .select('.ramp')
      .attr('x', marginLeft)
      .attr('y', marginTop)
      .attr('width', width - marginLeft - marginRight)
      .attr('height', height - marginTop - marginBottom)
      .attr(
        'xlink:href',
        ramp(color.copy().domain(quantize(interpolate(0, 1), n))).toDataURL()
      );
  }

  // Sequential
  else if (color.interpolator) {
    svg
      .select('.bars')
      .selectAll('rect')
      .transition(t)
      .attr('opacity', 0)
      .remove();

    x = Object.assign(
      color
        .copy()
        .interpolator(interpolateRound(marginLeft, width - marginRight)),
      {
        range() {
          return [marginLeft, width - marginRight];
        },
      }
    );

    svg
      .select('.ramp')
      .attr('x', marginLeft)
      .attr('y', marginTop)
      .attr('width', width - marginLeft - marginRight)
      .attr('height', height - marginTop - marginBottom)
      .attr('xlink:href', ramp(color.interpolator()).toDataURL())
      .attr('display', 'visible')
      .transition(t)
      .attr('opacity', 1);

    // scaleSequentialQuantile doesn’t implement ticks or tickFormat.
    if (!x.ticks) {
      if (tickValues === undefined) {
        const n = Math.round(ticks + 1);
        tickValues = range(n).map((i) => quantile(color.domain(), i / (n - 1)));
      }
      if (typeof tickFormat !== 'function') {
        tickFormat = format(tickFormat === undefined ? ',f' : tickFormat);
      }
    }
  }

  // Threshold
  else if (color.invertExtent) {
    const thresholds = color.thresholds
      ? color.thresholds() // scaleQuantize
      : color.quantiles
      ? color.quantiles() // scaleQuantile
      : color.domain(); // scaleThreshold

    const thresholdFormat =
      tickFormat === undefined
        ? (d) => d
        : typeof tickFormat === 'string'
        ? format(tickFormat)
        : tickFormat;

    x = scaleLinear()
      .domain([-1, color.range().length - 1])
      .rangeRound([marginLeft, width - marginRight]);

    svg
      .append('g')
      .selectAll('rect')
      .data(color.range())
      .join('rect')
      .attr('x', (d, i) => x(i - 1))
      .attr('y', marginTop)
      .attr('width', (d, i) => x(i) - x(i - 1))
      .attr('height', height - marginTop - marginBottom)
      .attr('fill', (d) => d);

    tickValues = range(-1, thresholds.length);
    tickFormat = (i) => {
      if (i === -1) return thresholdFormat(1);
      else if (i === thresholds.length - 1) return;
      else if (i === thresholds.length - 2)
        return thresholdFormat(thresholds[i] + '+', i);
      return thresholdFormat(thresholds[i], i);
    };
  }

  // Ordinal
  else {
    svg
      .select('.ramp')
      .transition(t)
      .attr('opacity', 0)
      .attr('xlink:href', null);
    if (!ordinalWeights) {
      x = scaleBand()
        .domain(color.domain().filter((d) => d))
        .rangeRound([marginLeft, width - marginRight]);
      svg
        .select('.bars')
        .selectAll('rect')
        .data(color.domain().filter((d) => d))
        .join('rect')
        .attr('x', x)
        .attr('y', marginTop)
        .attr('width', Math.max(0, x.bandwidth() - 1))
        .attr('height', height - marginTop - marginBottom)
        .attr('fill', color);
    } else {
      const widthScale = scaleLinear()
        .domain([0, ordinalWeights.reduce((a, b) => a + b)])
        .rangeRound([0, width - marginLeft - marginRight]);

      const xPos = ordinalWeights.map((w, i) =>
        ordinalWeights
          .slice(0, i)
          .reduce((acc, w) => acc + widthScale(w), marginLeft)
      );

      x = scaleOrdinal().domain(color.domain()).range(xPos);

      svg
        .select('.bars')
        .selectAll('rect')
        .data(color.domain())
        .join((enter) =>
          enter
            .append('rect')
            .attr('x', x)
            .attr('width', (d, i) => widthScale(ordinalWeights[i]))
        )
        .attr('y', marginTop)
        .attr('height', height - marginTop - marginBottom)
        .attr('fill', color)
        .transition(t)
        .attr('x', x)
        .attr('width', (d, i) => widthScale(ordinalWeights[i]))
        .attr('opacity', 1);
    }

    tickAdjust = () => {};
  }

  svg
    .select('.axis')
    .attr('transform', `translate(0,${height - marginBottom})`)
    .transition(t)
    .attr('class', 'axis')
    .call(
      axisBottom(x)
        .ticks(ticks, typeof tickFormat === 'string' ? tickFormat : undefined)
        .tickFormat(typeof tickFormat === 'function' ? tickFormat : undefined)
        .tickSize(tickSize)
        .tickValues(tickValues)
    )
    .on('start', () => {
      svg.call(tickAdjust).call((svg) => svg.select('.domain').remove());
    })
    .call((g) =>
      g
        .select('.axistext')
        .attr('class', 'axistext')
        .attr('x', marginLeft)
        .attr('y', marginTop + marginBottom - height - 6)
        .attr('fill', 'currentColor')
        .attr('text-anchor', 'start')
        .attr('font-weight', 'bold')
        .text(title)
    );

  return svg.node();
}