d3.js scatterplot and heat (density) chart

d3.js example showing a scatterplot with 500-50,000 random points and its associated density heat chart

by ramnathv

HTML

<script src="https://github.com/mbostock/d3/raw/master/d3.js"></script>
<select onchange="onNumPointsChange(event);">
    <option value="500">500</option>
    <option value="1500" selected="true">1500</option>
    <option value="5000">5000</option>
    <option value="15000">15,000</option>
    <option value="25000">25,000</option>
    <option value="50000">50,000</option>
</select> points

<button onclick="onRandomizeClick();">Randomize!</button>

<div id="plots">
    <div id="scatterContainer">
        <div id="scatterplot"></div>
        
        <input type="checkbox" checked="true" onchange="onShowScatterplotChange(event);">show scatterplot</input>
    </div>
    
    <div id="heatchartContainer">
        <div id="heatchart"></div>
    </div>            
    
</div>

CSS

div#plots {
    margin-bottom: 15px;
}

div#scatterplot, div#heatchart {
    border: 1px solid #ccc;
    margin-bottom: 10px;
}

div#scatterContainer, div#heatchartContainer {
    float: left;
    margin-left: 15px;
    margin-bottom: 20px;
}

button {
    margin-left: 15px;
    margin-bottom: 15px;
}

circle {
    fill: #09c;
}

select {
    margin-left: 15px;
}

JavaScript

var numPoints = 1500,
    size = 300,
    numRows = 16,
    numCols = 16,
    showingScatter = true,
    scatterDirty = false,
    data = null,
    cells = null,
    color = d3.interpolateRgb("#fff", "#c09");

var getEmptyCells = function() {
    var emptyCells = [];
    for (var rowNum = 0; rowNum < numRows; rowNum++) {
        emptyCells.push([]);
        var row = emptyCells[emptyCells.length - 1];
        for (var colNum = 0; colNum < numCols; colNum++) {
            row.push({
                row: rowNum,
                col: colNum,
                density: 0,
                points: []
            });
        }
    }
    return emptyCells;
};

var clearCells = function() {
    for (var rowNum = 0; rowNum < numRows; rowNum++) {
        for (var colNum = 0; colNum < numCols; colNum++) {
            cells[rowNum][colNum].density = 0;
            cells[rowNum][colNum].points = [];
        }
    }
};

var randomizeData = function() {
    data = [];

    if (cells === null) {
        cells = getEmptyCells();
    }
    else {
        clearCells();
    }

    var x, y, col, row;
    for (var i = 0; i < numPoints; i++) {
        x = Math.random() * size;
        y = Math.random() * size;
        col = Math.min(Math.floor(x / size * numCols), numCols - 1);
        row = Math.min(Math.floor(y / size * numRows), numRows - 1);

        data.push({
            x: x,
            y: y,
            col: col,
            row: row,
            cell: cells[row][col],
            ind: i
        });

        cells[row][col].points.push(data[data.length - 1]);
    }
};

var selectPoints = function(points) {
    d3.selectAll(points).attr("r", 4).attr("stroke", "#f00").attr("stroke-width", 3);

    for (var i = 0; i < points.length; i++) {
        points[i].parentNode.appendChild(points[i]);
    }
};

var deselectPoints = function(points) {
    d3.selectAll(points).attr("r", 2).attr("stroke", "none");
};

var selectCell = function(cell) {
    d3.select(cell).attr("stroke",...