Function: shotCells()
shotCells(
cells,o):Markish
Defined in: packages/sdvplot/src/plot/shots.ts:166
Data-space hexagons or squares (the dual encoding: r from sizeCells, fill from diffScale) drawn as
Plot.geo through the plot's x/y scales, so they sit on a sporty court at any orientation. Fewest attempts first,
so busy cells stay on top (blazing-the-nets main lib/charts/hexShotChart.ts:79); r = 0 is not drawn, nor
(by default, dropOutside) a cell centred outside the plot's frame. Each path carries data-sdv-id="x,y", the cell's
legacy centre. Marks paint in array order: after ...court.marks the cells cover the court lines under them, as
in main.
tip: true shows attempts, FG%, league FG% and the shrunk difference (formats .1%, +.1%; a tip object's
format overrides them key by key); every other
Plot.geo option passes through, and the cells are the mark's data, so fx/fy, filter, sort (replacing the
default order) and title read the caller's fields. A transform or initializer that makes new rows
(Plot.group, Plot.hexbin) throws InputError.
Parameters
cells
readonly CellVsLeague[]
o
Returns
Markish
Example
import * as Plot from "@observablehq/plot";
import { shotCells, surface } from "@sportsdataverse/sdvplot/plot";
import { cellsVsLeague, leagueIndex } from "@sportsdataverse/sdvplot/shots";
// Brooklyn shots from the sportsdataverse-data nba_stats_shots release, 2025-26
const shots = [
{ x_legacy: -1, y_legacy: 7, shot_distance: 1, shot_value: 2, shot_result: "Made" },
{ x_legacy: 0, y_legacy: 0, shot_distance: 0, shot_value: 2, shot_result: "Missed" },
{ x_legacy: -44, y_legacy: 252, shot_distance: 26, shot_value: 3, shot_result: "Made" },
];
// the three shots stand in for the league; the gallery's hex chart uses the 2025-26 league's index
const cells = cellsVsLeague(shots, leagueIndex(shots, 15));
const court = surface("nba", { displayRange: "defense" });
Plot.plot({ ...court.scales, marks: [...court.marks, shotCells(cells, { r: 15 })] });