npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

d3-area-label

v1.6.0

Published

A library for placing labels in areas.

Downloads

1,022

Readme

d3-area-label

A library for placing labels in areas.

image

image

image

You can use this to position labels on a StreamGraph or Stacked Area Chart.

Example usage:

const labels = svg.selectAll('text').data(stacked)
labels
  .enter().append('text')
    .attr('class', 'area-label')
  .merge(labels)
    .text(d => d.key)
    .attr('transform', d3.areaLabel(area)) // <---------- Call the function like this.

For more details and context, see test/index.html or run the example on bl.ocks.org.

How it Works

The label placement algorithm works as follows:

  • Measure the width and height of the bounding box of the text.
  • Use the bisection method to search for the maximum size rectangle that fits within the area and has the same aspect ratio as the measured bounding box.
    • For each iteration of the bisection method (where a specific size is given for testing), loop through all X coordinates that may potentially be used as the left edge of the label bounding box and perform the following test.
      • For a given X coordinate to be used as the left edge of the label, x0, find the first X coordinate that falls after the right edge of the label x1. For each X coordinate x between (and including) x0 and x1, compute the ceiling and floor to be the lowest Y coordinate of the top curve of the area and the highest Y coordinate of the bottom curve of the area, respectively.
        • If at any point (ceiling - floor) < height, where height is the height of the label bounding box being tested, break out of this iteration and move on to testing the next X coordinate.
        • If (ceiling - floor) >= height after having checked all x between x0 and x1, return the current x value as the solution. Note Only the first solution found is returned, no attempt is made to optimize this solution, because the optimization occurs at the level of scale choice; the bisection method will converge to a scale for which there is only 1 or very few solutions.
      • If no solution was found after having looped through all available X coordinates to be used as the left edge of the label, return false.

The set of possible X coordinate to be used as the left edge of the label depends on how interpolate and interpolateResolution are configured. If interpolation is turned off, the original X coordinates from the data points are the only coordinates considered for label placement. For datasets where there are large gaps between X coordinates, we can improve label placement by turning on interpolation, which will generate a certain number (interpolateResolution) of evenly spaced X coordinates and use linear interpolation to compute the corresponding Y coordinates for the top and bottom of the area curve. Cranking up the interpolateResolution value leads to more optimal label placement at the cost of more computation.

Installing

If you use NPM, npm install d3-area-label. Otherwise, download the latest release.

API Reference

# d3.areaLabel([area])

Constructs a new label position generator.

If area is specified, invokes areaLabel.area as well (equivalent to d3.areaLabel().area(area)).

# areaLabel(data)

Invoke the label position generator with the given data array.

This function computes the optimal position and size for a label and returns an SVG transform string.

# areaLabel.area(area)

Sets the x, y0, and y1 accessors applied to the data array from the given area, an instance of d3.area.

# areaLabel.x(x)

If x is specified, sets the x accessor applied to the data array and returns the label position generator. If x is not specified, returns the current x.

# areaLabel.y0(y0)

If y0 is specified, sets the y0 accessor applied to the data array and returns the label position generator. If y0 is not specified, returns the current y0.

# areaLabel.y1(y1)

If y1 is specified, sets the y1 accessor applied to the data array and returns the label position generator. If y1 is not specified, returns the current y1.

# areaLabel.minHeight(minHeight)

The minimum label bounding box height in pixels. Default is 2.

# areaLabel.epsilon(epsilon)

The tolerance within we wish to optimize the bounding box height (in pixels). Default is 0.01;

# areaLabel.maxIterations(maxIterations)

The maximum number of iterations for the bisection method algorithm, which is used to find the maximum height rectangle that fits within the area.

# areaLabel.interpolate(interpolate)

A boolean value that determines whether or not linear interpolation is used for computing label positions.

If set to false, only X coordinates that correspond to data points are considered for use as the leftmost position of the label bounding box. In cases where there is a high number of evenly spaced data points, a value of false works quite well. When there is a low number of data points, a value of false leads to embarassingly pathetic label placements.

If set to true, then a fixed number of X coordinates (interpolateResolution to be exact), not necessarily corresponding to data points, are considered for use as the leftmost position of the label bounding box. The upper and lower Y values for those X values are imputed from the nearest X values from the data using linear interpolation. When there is a low number of data points, a value of true improves label placement by leaps and bounds. A value of true also leads to more expensive computation for placing labels, so if you're encountering performance problems, try setting this to false.

Default is true.

# areaLabel.interpolateResolution(interpolateResolution)

The integer number of possible X positions to check for placing the leftmost edge of the label bounding box. The X extent of the area is subdivided evenly into this many points. When each point is checked, linear interpolation is used to estimate the data value. Default is 200.

This only comes into effect if interpolate is set to true.

# areaLabel.paddingLeft(paddingLeft)

The left padding for labels. This should be a value between 0 and 1. Default is 0.

# areaLabel.paddingRight(paddingRight)

The right padding for labels. This should be a value between 0 and 1. Default is 0.

# areaLabel.paddingTop(paddingTop)

The top padding for labels. This should be a value between 0 and 1. Default is 0.

# areaLabel.paddingBottom(paddingBottom)

The bottom padding for labels. This should be a value between 0 and 1. Default is 0.

# areaLabel.paddingX(paddingX)

A convenience method for simultaneously setting paddingLeft and paddingRight.

# areaLabel.paddingY(paddingY)

A convenience method for simultaneously setting paddingTop and paddingBottom.

# areaLabel.padding(padding)

A convenience method for simultaneously setting paddingX and paddingY.

Thanks

Many thanks to Lee Byron, Noah Veltman, Philippe Rivière, and Adam Pearce for ideas and input.