UNPKG

@gramex/radar

Version:

A radial beeswarm or dot plot.

262 lines (189 loc) 8.09 kB
# @gramex/radar A radial beeswarm or dot plot. ## Example Given this [100 most popular movies on IMDb](docs/imdb.csv ":ignore"): [![Table of 100 movies with name, genre, year, rating, and votes on IMDb](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/imdb.png)](docs/imdb.csv ":ignore") ... we can render the following radar: [![IMDb radar visual - playground](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/playground.png)](docs/playground.html ":include height=420px") ## Installation Install via `npm`: ```bash npm install @gramex/radar@1 ``` Use locally as an ES module: ```html <script type="module"> import { radar } from "./node_modules/@gramex/radar/dist/radar.js"; </script> ``` Use locally as a script: ```html <script src="./node_modules/@gramex/radar/dist/radar.min.js"></script> <script> gramex.radar(...) </script> ``` Use via CDN as an ES Module: ```html <script type="module"> import { radar } from "https://cdn.jsdelivr.net/npm/@gramex/radar@1"; </script> ``` Use via CDN as a script: ```html <script src="https://cdn.jsdelivr.net/npm/@gramex/radar@1/dist/radar.min.js"></script> <script> gramex.radar(...) </script> ``` ## Data is a list of levels and spokes The data is a list of rows containing an ordered "level" category (going outwards) and an unordered "spoke" category . Each row is an object with keys for each column. For example: ```js const data = [ { level: "low", spoke: "blue" }, { level: "low", spoke: "orange" }, { level: "low", spoke: "green" }, { level: "medium", spoke: "blue" }, { level: "medium", spoke: "orange" }, { level: "medium", spoke: "green" }, { level: "high", spoke: "blue" }, { level: "high", spoke: "orange" }, { level: "high", spoke: "green" }, ]; ``` [![Example](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/data.png)](docs/data.html ":include height=420px") [See how to use radar](docs/data.html ":include :type=code") Calling `radar(el, { data })` will render the radar. You need to style the output. ## Specify level and spoke columns Use `level` and `spoke` parameters to customize the columns used for levels and spokes. For example, this [employees data](docs/employees.csv ":ignore") has: | team | band | | ----------- | ------ | | Analytics | Band 1 | | Consulting | Band 2 | | Design | Band 3 | | Engineering | Band 4 | | ... | ... | We use the `level` and `spoke` parameters to specify the columns: ```js const graph = radar("#radar", { data, level: (d) => d.band.replace(/Band /, ""), spoke: (d) => d.team, ... }); ``` The radar automatically adjusts the [![Example](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/employees.png)](docs/employees.html ":include height=420px") [See how to specify level and spoke columns](docs/employees.html ":include :type=code") ## Re-order levels and spokes Radar auto-extracts levels and spokes from data. Use `levels` and `spokes` to: - Show only specific levels and spokes (even if they don't exist in the data) - Re-order levels and spokes For example: ```js const graph = radar("#radar", { data, // Show only the top 3 levels, in descending order levels: ["6", "5", "4"], // Show these levels in order, even though Legal doesn't exist spokes: ["Design", "Consulting", "Engineering", "Legal"], ... }); ``` You can specificy `levels` and `spokes` dynamically with a function `Map(key, count) => [...keys]`. For example: ```js const graph = radar("#radar", { data, // Sort levels in reverse (e.g. "6", "5", "4", ... "1") levels: (counter) => [...counter.keys()].sort(d3.descending), // Sort spokes in descending order of count, i.e. largest first spokes: (counter) => d3.sort([...counter.keys()], d => -counter.get(d)), ... }); ``` [![Example](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/reorder.png)](docs/reorder.html ":include height=420px") [See how to customize levels and spokes](docs/reorder.html ":include :type=code") ## Resize levels and spokes Radar distributes levels and spokes evenly within the container circle. Use `levelScale` and `spokeScale` to: - Create spacing between levels or spokes - Give more space to specific levels or spokes For example: ```js // Start and end radius in SVG units (pixels) const levelRange = [[0, 60], [66, 120], [125, 140], [145, 160], [165, 180], [185, 200]]; // Start and end angle in degrees const spokeRange = [[180, 210], [210, 240], [240, 255], [255, 310], [310, 330], [330, 360]] const graph = radar("#radar", { data, levelScale: (level, index) => levelRange[index], spokeScale: (spoke, index) => spokeRange[index], ... }); ``` You can also specify the center of the radar using `cx` and `cy`. It defaults to `width / 2` and `height / 2`. ```js const graph = radar("#radar", { data, cx: 100, cy: 100, ... }); ``` Use `graph.r` to get the maximum non-overlapping radius of the nodes. [![Example](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/resize.png)](docs/resize.html ":include height=220px") [See how to resize levels and spokes](docs/resize.html ":include :type=code") ## Style the radar The returned object has the following D3 selections: - `cells`: Arc paths for each level, spoke segment. (Used to style the background of the radar). The data includes: - `level`: The level - `spoke`: The spoke - `levelIndex`: The index of the level (0, 1, 2, ...) - `spokeIndex`: The index of the spoke (0, 1, 2, ...) - `r0`: The inner radius of the spoke - `r1`: The outer radius of the spoke - `a0`: The start angle of the spoke in radians - `a1`: The end angle of the spoke in radians - `n`: Number of data points in this cell - `data`: All data elements for this cell (i.e. level and spoke combination). Absent if n == 0 - `nodes`: Circles for each data element. The data includes: - `x`: The x-coordinate of the node - `y`: The y-coordinate of the node - `levelLabels`: Text labels for each level. The data includes: - `level`: The level name / label - `levelIndex`: The index of the level (0, 1, 2, ...) - `r0`: The inner radius of the spoke - `r1`: The outer radius of the spoke - `spokeLabels`: Text labels for each spoke. The data includes: - `spoke`: The spoke name / label - `spokeIndex`: The index of the spoke (0, 1, 2, ...) - `a0`: The start angle of the spoke in radians - `a1`: The end angle of the spoke in radians You can style these using D3 selections. For example: ```js // Color empty background cells pink, and filled cells white graph.cells.attr("fill", (d) => (d.n ? "white" : "pink")); // Color nodes with see-through black. Give them a bit more than the maximum non-overlapping size graph.nodes.attr("fill", "rgba(0,0,0,0.2)").attr("r", graph.r * 1.5); // Make labels bigger and red graph.levelLabels.attr("font-size", 12).attr("fill", "red"); graph.spokeLabels.attr("font-size", 12).attr("fill", "red"); ``` [![Example](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/style.png)](docs/style.html ":include height=420px") [See how to style the radar](docs/style.html ":include :type=code") ## Add tooltips You can use [Bootstrap tooltips](https://getbootstrap.com/docs/5.3/components/tooltips/). 1. Add a `data-bs-toggle="tooltip" title="..."` attribute to each feature using `update` 2. Call `new bootstrap.Tooltip(element, {selector: '[data-bs-toggle="tooltip"]'})` to initialize tooltips [![Example](https://code.gramener.com/cto/gramex-radar/-/raw/main/docs/imdb.png)](docs/imdb.html ":include height=420px") [See how to add tooltips](docs/imdb.html ":include :type=code") ## Bring your own D3 If you already have D3 loaded, or want to use a specific version / instance of D3, pass it to `radar(el, { d3 })`: ## API [See API documentation](docs/api.md ":include :type=markdown") ## Release notes - 1.0.0: 24 Mar 2024. Initial release ## Authors - Anand S <s.anand@gramener.com> ## License [MIT](https://spdx.org/licenses/MIT.html)