0

I have been struggling with this quite sometimes, really need help with this. I wanna to create a heatmap with it content and axis are all scalable and zoom-able accordingly.Notice I have converted the svg content to canvas. I suppose I have already halfway there, unfortunately I'am unable to make the canvas zoom and drag accordingly, meanwhile the axis itself is okay.

Below is my source, your help is really needed. Thanks and sorry for my bad grammar.

$(document).ready(function() {
    let w = $('#bodyBackground')[0];
    let clientWidthz = w.clientWidth;
    let clientHeightz = w.clientHeight;
    var radars = [];
    var newSample = [];
    var timeStampLegends = [];

    for (var i = 0; i < 1440; i++) {
        var legend_xTime = new Date(Date.now() - (i * 60 * 1000));
        timeStampLegends.push(legend_xTime);
    }
    for (var unit_i = 0; unit_i <= 101;) {
        if (unit_i == 0) {
            radars.push(1);
            unit_i = unit_i + 5;
        } else {
            radars.push(unit_i);
            unit_i = unit_i + 4;
        }
    }
    //by using below method we can observe the delay is not due to the data during insertion
    for (var unit = 1; unit <= 5; unit++) {
        timeStampLegends.forEach(function(dbData) {
            var i = Math.random() * 1400;
            newSample.push({ radars: unit, timestamp: dbData, level: i });
        });
    }
    var margin = { top: 20, right: 20, bottom: 30, left: 60 };
    var width = 1200 - margin.left - margin.right;
    var height = 500 - margin.top - margin.bottom;

    var x = d3.scaleTime()
        .domain([new Date(timeStampLegends[0]), new Date(timeStampLegends[timeStampLegends.length - 1])])
        .range([0, width]);

    var min = d3.min(newSample, function(d) {
        return d.radars;
    });

    var max = d3.max(newSample, function(d) {
        return d.radars;
    });

    var y = d3.scaleLinear()
        .domain([min, max])
        .range([0, height]);

    var xAxis = d3.axisTop(x).tickSize(height); //the size of the inner grid line (vertical - x-axis)

    var yAxis = d3.axisLeft(y).ticks(5).tickSize(-width); //the size of the inner grid line (horizontal - y - axis)

    var zoom = d3.zoom()
        .translateExtent([
            [0, 0],
            [width + 2000, height]
        ])
        .scaleExtent([1, 10])
        .on("zoom", zoomed);

    var svg = d3.select(".TrafficCongestions").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var canvas = d3.select('.TrafficCongestions').append("canvas")
        .attr("id", "canvas")
        //absolute: a feature that allows any sort of element
        //to be positioned disregards to other elements
        //i.e. it will calculate its own position again from (0,0)
        .style("position", "absolute")
        .style("top", "20px")
        .style("left", "60px")
        .attr("width", width + margin.left + margin.right) //optimization
        .attr("height", height + margin.top + margin.bottom);//optimization

    var context = canvas.node().getContext("2d");
    context.clearRect(0, 0, width, height);
    var detachedContainer = document.createElement("custom");
    var dataContainer = d3.select(detachedContainer);   

    var colorScale = d3.scaleLinear().domain([0, 600, 1200]).range(["#009933", "#FFCC00", "#990000"]);

    //x-axis (solely based on data of times)
    var timeLabels = svg.append("g")
        .attr("class", "x-axis")
        .attr("transform", "translate(0," + (height) + ")")
        .style("font-size", "20px")
        .call(xAxis);

    //y-axis (solely based on data of radars)
    var radarLabels = svg.append("g")
                        .attr("class", "y-axis")
                        .call(yAxis);

    //console.log(JSON.stringify(newSample));
    var heatMap = dataContainer.selectAll(".custom.rect")
                .data(newSample)
                .enter().append("custom")
                .attr("x", function(d){
                    return x(d.timestamp);
                })
                .attr("y", function(d){
                    return y(d.radars);
                })
                .classed("rect", true)
                .attr("class", "rect bordered")
                .attr("width", function(d){
                    var offSetX = x(d3.timeMinute.offset(d.timestamp, 1))- x(d.timestamp);
                    return offSetX;
                })
                //.attr("width", "1120px")
                .attr("height", function(d){
                    var offSetY = y(d.radars - 1) - y(d.radars);
                    return offSetY;
                })
                //.attr("strokeStyle", "rgba(255,255,255,0.2)")
                .attr("fillStyle", function(d, i){
                    return colorScale(d.level);
                });

    canvas.call(zoom);

    drawCanvas();

    function drawCanvas(){
        var elements = dataContainer.selectAll("custom.rect");

        elements.each(function(d){
            var node = d3.select(this);

            context.beginPath();
            context.fillStyle = node.attr("fillStyle");
            context.rect(node.attr("x"), node.attr("y"), node.attr("width"), node.attr("height"));
            context.fill();
            context.closePath();
        });
    }

    function zoomed() {
        var currentTransform = d3.event.transform;
        // update: rescale x axis
        timeLabels.call(xAxis.scale(d3.event.transform.rescaleX(x)));
        update();
    }

    function update() {
        // update: cache rescaleX value
        var rescaleX = d3.event.transform.rescaleX(x);
        heatMap.selectAll(".custom.rect")
            // update: apply rescaleX value
            .attr("x", function(d) {
                return rescaleX(d.timestamp);
            })
            .attr("y", function(d) {
                return y(d.radars);
            })
            // update: apply rescaleX value
            .attr("width", function(d) {
                return rescaleX(d3.timeMinute.offset(d.timestamp, 1)) - rescaleX(d.timestamp);
                //return rescaleX(x);
            })
            .attr("height", function(d) {
                return y(d.radars - 1) - y(d.radars);
            })
            //.attr("strokeStyle", "rgba(255,255,255,0.2)")
            .attr("fillStyle", function(d, i){
                    return colorScale(d.level);
            });

        drawCanvas();
    }
});
body {
  position: relative;
  width: 960px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
<script
  src="https://code.jquery.com/jquery-1.10.0.min.js"
  integrity="sha256-2+LznWeWgL7AJ1ciaIG5rFP7GKemzzl+K75tRyTByOE="
  crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<html>
<head>
    <title></title>
</head>

<body id="bodyBackground">
 <div class="TrafficCongestions"></div>

</body>
</html>
cypatrick
  • 151
  • 1
  • 11
  • [Zoom on canvas example](https://bl.ocks.org/mbostock/3680958) – pmkro Mar 21 '18 at 15:54
  • 1
    In your update function: `heatMap.selectAll(".custom.rect")` won't select anything, so you won't update anything. Try changing it to: dataContainer.selectAll("custom.rect") – Andrew Reid Mar 21 '18 at 17:25
  • @AndrewReid thank you so much. Was thinking `heatMap` has been declared earlier, I can reused it. Thanks a lot you save my day. – cypatrick Mar 23 '18 at 02:23

0 Answers0