简体   繁体   中英

Amcharts Sort stackbyvalue with bullet chart

I am using stackByValue of amcharts to arranged a stack column chart . I would like to add a bullet point on each chart to check if they meet a certain target or not. Currently what happen is the bullet point is added to the stacked chart is there a way that I could do this without removing the stackByValue ?

Here is my JsFiddle: ` http://jsfiddle.net/sky5rvdz/13/

$(document).ready(function() {
    AmCharts.addInitHandler(function(chart) {
        // Check if enabled
        if (chart.valueAxes === undefined || chart.valueAxes.length === 0 || !chart.valueAxes[0].stackByValue)
            return;

        // Disable built-in stacking
        chart.valueAxes[0].stackType = "none";

        // Prepare all graphs
        for (var i = 0; i < chart.graphs.length; i++) {
            var graph = chart.graphs[i];
            graph.originalValueField = graph.valueField;
            graph.valueField = graph.originalValueField + "Close";
            graph.openField = graph.originalValueField + "Open";
            graph.clustered = false;
            if (graph.labelText)
                graph.labelText = graph.labelText.split("[[value]]").join("[[" + graph.originalValueField + "]]");
            if (graph.balloonText)
                graph.balloonText = graph.balloonText.split("[[value]]").join("[[" + graph.originalValueField + "]]");
        }

        // Go through each category and order values
        for (var i = 0; i < chart.dataProvider.length; i++) {

            // Assemble intermediate array of data point items
            var dp = chart.dataProvider[i];
            var items = [];
            var sum = 0;
            for (var x = 0; x < chart.graphs.length; x++) {
                var graph = chart.graphs[x];
                items.push({
                    "graph": graph,
                    "value": dp[graph.originalValueField]
                });
            }

            var sortValue = 0;

            // Order according to value
            items.sort(function(a, b) {
                if (sortValue == 0) {

                    return a.value - b.value;
                } else {

                    return b.value - a.value;
                }

            });

            // Calculate open and close fields
            var offset = 0;
            for (var x = 0; x < items.length; x++) {
                var item = items[x];
                dp[item.graph.openField] = offset;
                dp[item.graph.valueField] = offset + dp[item.graph.originalValueField];
                offset = dp[item.graph.valueField];
            }
        }
    }, ["serial"]);

    var response = [{
        "name": "Jan",
        "target": 2062186.74,
        "USA": 0,
        "MAN": 605873.95,
        "PAN": 759763.5
    }, {
        "name": "Feb",
        "target": 1492210.81,
        "MAN": 499538.43,
        "PAN": 559504.95,
        "USA": 5850
    }, {
        "name": "Mar",
        "target": 1455750,
        "MAN": 403715.2,
        "PAN": 694353.95,
        "USA": 0
    }, {
        "name": "Apr",
        "target": 2008623.96,
        "USA": 0,
        "MAN": 409993.3,
        "PAN": 511030
    }];

    var graphs = Object.keys(response[0]).reduce(function(graphsArray, key) {
        if (key !== "name" && key !== "target") {
            graphsArray.push({
                "balloonText": "<b>[[value]]</b>",
                "balloonFunction": function(item, graph) {
                    var result = graph.balloonText;
                    for (var key in item.dataContext) {
                        if (item.dataContext.hasOwnProperty(key) && !isNaN(item.dataContext[key])) {
                            var formatted = AmCharts.formatNumber(item.dataContext[key], {
                                precision: chart.precision,
                                decimalSeparator: chart.decimalSeparator,
                                thousandsSeparator: chart.thousandsSeparator
                            }, 2);
                            result = result.replace("[[" + key + "]]", formatted);
                        }
                    }
                    return result;
                },
                "fillAlphas": 0.8,
                "labelText": "[[title]]<br>",
                "labelPosition": "middle",
                "lineAlpha": 0.3,
                "title": key,
                "type": "column",
                "color": "#000000",
                //"showAllValueLabels": true,
                "valueField": key
            });
        }
        if (key === "target") {
            graphsArray.push({
                "balloonText": "<b>[[value]]</b>",
                "balloonFunction": function(item, graph) {
                    var result = graph.balloonText;
                    for (var key in item.dataContext) {
                        if (item.dataContext.hasOwnProperty(key) && !isNaN(item.dataContext[key])) {
                            var formatted = AmCharts.formatNumber(item.dataContext[key], {
                                precision: chart.precision,
                                decimalSeparator: chart.decimalSeparator,
                                thousandsSeparator: chart.thousandsSeparator
                            }, 2);
                            result = result.replace("[[" + key + "]]", formatted);
                        }
                    }
                    return result;
                },
                "valueAxis": "v2",
                "lineAlpha": 0,
                "bullet": "round",
                "bulletSize": 20,
                "title": "target",
                "type": "line",

                "valueField": "target"
            });
        }
        return graphsArray;
    }, []);

    var chart = AmCharts.makeChart("chartdiv", {
        "type": "serial",
        "theme": "light",
        "legend": {
            "horizontalGap": 10,
            "maxColumns": 1,
            "position": "right",
            "useGraphSettings": true,
            "markerSize": 10
        },
        "numberFormatter": {
            "precision": 1,
            "decimalSeparator": ".",
            "thousandsSeparator": ","
        },
        "dataProvider": response,
        "valueAxes": [{
            "id": "v1",
            "stackType": "regular",
            /**
             * A proprietary setting `stackByValue` which is not an
             * official config option. It will be used by our custom
             * plugin
             */
            "stackByValue": true,
            "axisAlpha": 0.3,
            "gridAlpha": 0
        }, , {
            "id": "v2",
            "axisAlpha": 0.3,
            "gridAlpha": 0,
            "position": "top",

            "title": "Target"
        }],
        "gridAboveGraphs": true,
        "startDuration": 0,
        "graphs": graphs,
        "categoryField": "name",
        "categoryAxis": {
            "gridPosition": "start",
            "axisAlpha": 0,
            "gridAlpha": 0,
            "position": "left"
        },
        "export": {
            "enabled": true
        }

    });

    console.log(graphs);
    console.log(response);

    Object.keys(response[0]).forEach(key => {
        console.log(key) // returns the keys in an object
        // console.log(a[key])  // returns the appropriate value 
    })
});

The issue is that the sort by value plugin assumes that all graphs need to be sorted and modified to use the open/close fields to achieve this effect, which causes it to move your bullet to an incorrect location. Since you have multiple axes, you can modify the plugin to check if the graph belongs to the first axis and set a flag to be used to re-add the point correctly:

  // Go through each category and order values
  for (var i = 0; i < chart.dataProvider.length; i++) {

    // ...
    for (var x = 0; x < chart.graphs.length; x++) {
      var graph = chart.graphs[x];
      items.push({
        "graph": graph,
        // check if this graph's data points need to be omitted from the sorting process.
        "ignoreSort": (graph.valueAxis && graph.valueAxis !== chart.valueAxes[0].id),
        "value": dp[graph.originalValueField]
      });
    }

    // ...

    // Calculate open and close fields
    var offset = 0;
    for (var x = 0; x < items.length; x++) {
      var item = items[x];

      if (!item.ignoreSort) {
        //process the pont as normal if it doesn't have the flag set with open/value fields
        dp[item.graph.openField] = offset;
        dp[item.graph.valueField] = offset + dp[item.graph.originalValueField];
        offset = dp[item.graph.valueField];
      } else {
        //otherwise treat the point as a normal graph and use the value field
        dp[item.graph.valueField] = dp[item.graph.originalValueField]
      }

    }
  }

You'll also want to synchronize the axes' min/max values so that your target is correctly placed with respect to your stacked bars. You can achieve this using another custom plugin through addInitHandler :

//synchronizes axes' min/max values using a custom synchronizeValueAxes property
//(while synchronizeGrid exists, it doesn't work with this particular chart)
AmCharts.addInitHandler(function(chart) {
  if (chart.synchronizeValueAxes) {
    setTimeout(function() {
      var max = chart.valueAxes.reduce(function(max, axis) {
        if (!isNaN(axis.max)) {
          return Math.max(max, axis.max);
        } else {
          return max;
        }
      }, Number.MIN_VALUE);
      var min = chart.valueAxes.reduce(function(min, axis) {
        if (!isNaN(axis.min)) {
          return Math.min(min, axis.min);
        } else {
          return min;
        }

      }, Number.MAX_VALUE);

      chart.valueAxes.forEach(function(axis) {
        axis.maximum = max;
        axis.minimum = min;
        axis.strictMinMax = true;
      });
      chart.validateData();
    }, 500);
  }

}, ["serial"]);

Updated fiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM