Search code examples
jquerygridflotsymbolsaxes

Jquery flot - zero value exceeds grid when changing axes direction


I have a problem with a jQuery flot and jquery.flot.symbol usage. I render a flot using jquery.flot and using an additional plugin (jquery.flot.symbol to make some rectangles over the graph).

The problem is when I reverse the axes (ox starting from right to left and oy position right). The part that is generated with the help of jquery.flot.symbol exceeds the grid and the zero values is somehow outside the graph (with other words all the values are moved to right)

http://jsfiddle.net/5L3eyafn/2/

and a relevant part of the code

        var data1 = [
                    {
                        data: graphData,
                        bars: {
                            show: true,
                            lineWidth: 3,
                            horizontal: false,
                            fillColor: "#f8e7b3"
                        },
                        color: "#ffffff"
                    },
                    {
                        data: trendData,
                        points: {
                            show: true,
                            lineWidth: 3,
                            radius: 10,
                            fill: true,
                            fillColor: "#000000",
                            symbol: "rectangleGraph"
                        },
                        color: "#000000"
                    }
                ];

                $.plot($(".graph"), data1,  {
                    grid:{
                        tickColor: "#eee",
                        borderColor: "#eee",
                        borderWidth: 1,
                        hoverable: true,
                        clickable: true
                    },
                    xaxis:{
                        show: true,
                        transform: function (v) { return -v; },
                        inverseTransform: function (v) { return -v; }
                    },
                    yaxis: {
                        show: true,
                        position: "right"
                    }
                });

Solution

  • The problem here is that the bars are aligned left by default which switches to alignment right by your transformation to the reverse. This does not happen automatically with the symbols. Here you have to do it by hand.

    1) Changing symbol alignment to right like the bars (fiddle):

    Change

    ctx.rect(x + 3, y - 8, 12, 1); //12 - width, 1 - height
    

    to

    ctx.rect(x - 15, y - 8, 12, 1); //12 - width, 1 - height
    

    If you want to be able to switch between normal and reverse axis you have to use a variable for the x offset.

    2) Using center alignment (fiddle):

    Change the above line to

    ctx.rect(x - 6, y - 8, 12, 1); //12 - width, 1 - height
    

    and add this to the bars options:

    align: "center"
    

    With center alignment you can switch between normal and reverse without need to change the symbol code. (And it better shows which bar belongs to which x value.)

    3) Correction for symbol y values (fiddle):

    As you can see in the fiddles for both 1) and 2), the symbol for the value x = 0 is above the bar, but it should not be (symbol y = 41.6, bar y = 42.0).
    To correct this you can also change the y offset in the code line from above to

    ctx.rect(x - 6, y + 1, 12, 1); //12 - width, 1 - height
    

    Here is the result from 2) plus 3) as a code snippet:

    /*
    Flot plugin that adds some extra symbols for plotting points.
    
    The symbols are accessed as strings through the standard symbol
    choice:
    
      series: {
          points: {
              symbol: "square" // or "diamond", "triangle", "cross"
          }
      }
    
    */
    
    (function ($) {
        function processRawData(plot, series, datapoints) {
            // we normalize the area of each symbol so it is approximately the
            // same as a circle of the given radius
    
            var handlers = {
                square: function (ctx, x, y, radius, shadow) {
                    // pi * r^2 = (2s)^2  =>  s = r * sqrt(pi)/2
                    var size = radius * Math.sqrt(Math.PI) / 2;
                    ctx.rect(x - size, y - size, size + size, size + size);
                },
                diamond: function (ctx, x, y, radius, shadow) {
                    // pi * r^2 = 2s^2  =>  s = r * sqrt(pi/2)
                    var size = radius * Math.sqrt(Math.PI / 2);
                    ctx.moveTo(x - size, y);
                    ctx.lineTo(x, y - size);
                    ctx.lineTo(x + size, y);
                    ctx.lineTo(x, y + size);
                    ctx.lineTo(x - size, y);
                },
                triangle: function (ctx, x, y, radius, shadow) {
                    // pi * r^2 = 1/2 * s^2 * sin (pi / 3)  =>  s = r * sqrt(2 * pi / sin(pi / 3))
                    var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3));
                    var height = size * Math.sin(Math.PI / 3);
                    ctx.moveTo(x - size / 2, y + height / 2);
                    ctx.lineTo(x + size / 2, y + height / 2);
                    if (!shadow) {
                        ctx.lineTo(x, y - height / 2);
                        ctx.lineTo(x - size / 2, y + height / 2);
                    }
                },
                cross: function (ctx, x, y, radius, shadow) {
                    // pi * r^2 = (2s)^2  =>  s = r * sqrt(pi)/2
                    var size = radius * Math.sqrt(Math.PI) / 2;
                    ctx.moveTo(x - size, y - size);
                    ctx.lineTo(x + size, y + size);
                    ctx.moveTo(x - size, y + size);
                    ctx.lineTo(x + size, y - size);
                },
                rectangle: function (ctx, x, y, radius, shadow) {
                    // pi * r^2 = (2s)^2  =>  s = r * sqrt(pi)/2
                    var size = radius * Math.sqrt(Math.PI) / 2;
                    ctx.rect(x + 2, y - 8, 5, 8); //5 - width, 9 - height
                },
                rectangleGraph: function (ctx, x, y, radius, shadow) {
                    // pi * r^2 = (2s)^2  =>  s = r * sqrt(pi)/2
                    var size = radius * Math.sqrt(Math.PI) / 2;
                    //ctx.rect(x + 3, y - 8, 12, 1); //12 - width, 1 - height
                    ctx.rect(x - 6, y + 1, 12, 1); //12 - width, 1 - height
                }
            };
    
            var s = series.points.symbol;
            if (handlers[s]) series.points.symbol = handlers[s];
        }
    
        function init(plot) {
            plot.hooks.processDatapoints.push(processRawData);
        }
    
        $.plot.plugins.push({
            init: init,
            name: 'symbols',
            version: '1.0'
        });
    })(jQuery);
    // end of symbol plugin
    
    
    $(document).ready(function () {
        var graphData = [
            [0, "42.00"],
            [1, "42.00"],
            [2, "42.00"],
            [3, "42.00"],
            [4, "42.00"],
            [5, "42.00"],
            [6, "42.00"],
            [7, "42.00"],
            [8, "42.00"],
            [9, "42.00"],
            [10, "42.00"],
            [11, "42.00"],
            [12, "43.00"],
            [13, "43.00"],
            [14, "43.00"],
            [15, "43.00"],
            [16, "43.00"],
            [17, "43.00"],
            [18, "43.00"],
            [19, "43.00"],
            [20, "43.00"],
            [21, "43.00"],
            [22, "43.00"],
            [23, "42.00"],
            [24, "42.00"],
            [25, "42.00"],
            [26, "41.00"],
            [27, "41.00"],
            [28, "41.00"],
            [29, "41.00"],
            [30, "41.00"],
            [31, "41.00"],
            [32, "41.00"],
            [33, "41.00"],
            [34, "41.00"],
            [35, "41.00"],
            [36, "41.00"],
            [37, "40.00"],
            [38, "40.00"],
            [39, "41.00"],
            [40, "41.00"],
            [41, "40.00"]
        ];
    
        var trendData = [
            [0, "41.60"],
            [1, "39.93"],
            [2, "39.13"],
            [3, "38.87"],
            [4, "38.20"],
            [5, "37.93"],
            [6, "37.13"],
            [7, "36.73"],
            [8, "36.27"],
            [9, "36.07"],
            [10, "34.60"],
            [11, "35.20"],
            [12, "35.00"],
            [13, "33.53"],
            [14, "34.07"],
            [15, "33.47"],
            [16, "32.93"],
            [17, "31.40"],
            [18, "32.07"],
            [19, "30.07"],
            [20, "28.80"],
            [21, "29.00"],
            [22, "28.67"],
            [23, "29.20"],
            [24, "26.13"],
            [25, "28.40"],
            [26, "26.27"],
            [27, "25.07"],
            [28, "25.07"],
            [29, "24.53"],
            [30, "25.80"],
            [31, "20.40"],
            [32, "25.07"],
            [33, "23.27"],
            [34, "22.13"],
            [35, "22.07"],
            [36, "20.27"],
            [37, "22.07"],
            [38, "18.67"],
            [39, "19.13"],
            [40, "18.87"],
            [41, "18.67"]
        ];
    
        var data1 = [{
            data: graphData,
            bars: {
                show: true,
                lineWidth: 3,
                horizontal: false,
                fillColor: "#f8e7b3",
                align: "center"
            },
            color: "#ffffff"
        }, {
            data: trendData,
            points: {
                show: true,
                lineWidth: 3,
                radius: 10,
                fill: true,
                fillColor: "#000000",
                symbol: "rectangleGraph"
            },
            color: "#000000"
        }];
    
        $.plot($(".graph"), data1, {
            grid: {
                tickColor: "#eee",
                borderColor: "#eee",
                borderWidth: 1,
                hoverable: true,
                clickable: true
            },
            xaxis: {
                show: true,
                //*
                transform: function (v) {
                    return -v;
                },
                inverseTransform: function (v) {
                    return -v;
                }
                //*/
            },
            yaxis: {
                show: true,
                position: "right"
            }
        });
    });
    .graph {
        width: 800x;
        height: 300px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/flot/0.8.2/jquery.flot.min.js"></script>
    <div class="graph bar"></div>