Неправильный диапазон дат в наборах данных AmCharts неверен
Мы используем биржевую диаграмму от AmCharts с несколькими DataSets, загружаемыми асинхронно через ajax.
К сожалению, это выглядит как диапазон дат для всей диаграммы, я имею в виду, что "От" и "Кому" задается только из первого, добавленного в массив всех наборов данных.
Представьте себе ситуацию, когда у вас есть 3 набора данных, и у каждого из них может быть совершенно разная дата начала и окончания:
<script id="data-set-1" type="application/json">
[
{"date": "2016-10-02T10:00:00", "value": 23.8},
{"date": "2016-10-02T10:05:00", "value": 16.8},
{"date": "2016-10-02T10:10:00", "value": 20.5}
]
</script>
<!-- This dataset has max end date from all 3 datasets -->
<script id="data-set-2" type="application/json">
[
{"date": "2016-10-02T10:00:00", "value": 15.2},
{"date": "2016-10-02T10:05:00", "value": 21.4},
{"date": "2016-10-02T10:15:00", "value": 18.1}
]
</script>
<!-- This dataset has min start date from all 3 datasets -->
<script id="data-set-3" type="application/json">
[
{"date": "2016-10-02T09:55:00", "value": 12.4},
{"date": "2016-10-02T10:00:00", "value": 17.7},
{"date": "2016-10-02T10:05:00", "value": 14.6}
]
</script>
В приведенном выше примере вы можете видеть, что набор данных 2 имеет максимальную дату окончания из всех 3 наборов данных, а набор данных 3 имеет минимальную дату начала из всех 3 наборов данных.
Конечный результат:
<!DOCTYPE html>
<html>
<head>
<title>AmCharts multiple datasets min-max date range issue</title>
<meta charset="utf-8" />
<style type="text/css">
#chartdiv {
width: 100%;
height: 500px;
}
</style>
<script id="data-set-1" type="application/json">
[
{"date": "2016-10-02T10:00:00", "value": 23.8},
{"date": "2016-10-02T10:05:00", "value": 16.8},
{"date": "2016-10-02T10:10:00", "value": 20.5}
]
</script>
<!-- This dataset has max end date from all 3 datasets -->
<script id="data-set-2" type="application/json">
[
{"date": "2016-10-02T10:00:00", "value": 15.2},
{"date": "2016-10-02T10:05:00", "value": 21.4},
{"date": "2016-10-02T10:15:00", "value": 18.1}
]
</script>
<!-- This dataset has min start date from all 3 datasets -->
<script id="data-set-3" type="application/json">
[
{"date": "2016-10-02T09:55:00", "value": 12.4},
{"date": "2016-10-02T10:00:00", "value": 17.7},
{"date": "2016-10-02T10:05:00", "value": 14.6}
]
</script>
<link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />
</head>
<body>
<div id="chartdiv"></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/amstock.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
AmCharts.useUTC = true; // this will prevent adding timezone hours to ech date
var chart = AmCharts.makeChart("chartdiv", {
"pathToImages": "http://cdn.amcharts.com/lib/3/images/",
"type": "stock",
"theme": "light",
"categoryAxesSettings": {
"minPeriod": "mm" // precision to minutes
},
"dataSets": [], // empty, we will add each based on single sensor
"panels": [{
"recalculateToPercents": "never", // show value on scale not percent
"showCategoryAxis": true,
"title": "",
"percentHeight": 70,
"stockGraphs": [{ // generic config for all lines
"id": "g1",
"connect": true, // show gaps in data
"comparable": true, // must be true to disable/enable each dataset
"compareField": "value",
"valueField": "value",
"type": "smoothedLine",
"lineThickness": 2,
"bullet": "round"
}],
"stockLegend": {
"periodValueTextRegular": "[[value.close]]" // what will be shown at top legend
}
}],
"chartScrollbarSettings": {
"graph": "g1",
"usePeriod": "10mm",
"position": "top"
},
"chartCursorSettings": {
"valueBalloonsEnabled": true
},
"periodSelector": {
"fromText": "",
"toText": "",
"periodsText": "",
"position": "top",
"dateFormat": "YYYY-MM-DD JJ:NN",
"inputFieldWidth": 150,
"periods": [{
"period": "hh",
"count": 1,
"label": "1 H",
"selected": true
}, {
"period": "hh",
"count": 8,
"label": "8 H"
}, {
"period": "DD",
"count": 1,
"label": "1 D"
}, {
"period": "DD",
"count": 10,
"label": "10 D"
}, {
"period": "MM",
"selected": true,
"count": 1,
"label": "1 M"
}, {
"period": "YYYY",
"count": 1,
"label": "1 Y"
}, {
"period": "YTD",
"label": "YTD"
}, {
"period": "MAX",
"label": "MAX"
}]
},
"panelsSettings": {
"usePrefixes": true
},
"export": {
"enabled": true,
"exportTitles": true,
"libs": {
"path": "http://www.amcharts.com/lib/3/plugins/export/libs/"
},
"position": "bottom-right"
}
});
for (var i = 1; i <= 3; i++) {
var dataset = new AmCharts.DataSet();
dataset.compared = true;
dataset.title = "DataSet " + i;
dataset.categoryField = "date";
dataset.fieldMappings = JSON.parse('[{"fromField": "value", "toField": "value"}]');
var data = JSON.parse($("#data-set-" + i).html());
dataset.dataProvider = data;
chart.dataSets.push(dataset);
chart.validateData();
}
});
</script>
</body>
</html>
Вы можете видеть, что вся диаграмма усечена до диапазона дат из набора данных-1, диаграмма не показывает точку набора дат-2:
{"date": "2016-10-02T10:15:00", "value": 18.1}
а также не показывает дату-набор-3 балла:
{"date": "2016-10-02T09:55:00", "value": 12.4}
Я попытался взломать "От" и "Кому" входы без какой-либо удачи:
chart.addListener("init",
function (e) {
//e.chart.startDate = moment("2016-10-01 00:00");
//e.chart.endDate = moment("2016-10-03 00:00");
});
Есть идеи, как заставить диаграмму искать минимальные и максимальные даты во всех наборах данных, а не только в первом, добавленном в массив наборов данных?
1 ответ
Биржевая диаграмма всегда будет использовать диапазон дат из основного выбранного набора данных, игнорируя точки данных из сравниваемых наборов данных, которые не "вписываются" в этот диапазон.
Кроме того, он будет игнорировать точки данных, которые не имеют прямого совпадения меток времени.
Очевидное решение состоит в том, чтобы синхронизировать данные во всех наборах данных путем добавления "пустых" точек данных в основной набор данных, которые будут перекрывать точки в сравниваемых наборах данных.
Я знаю, что это может быть неприятно неудобно и неэффективно делать это на стороне сервера, где генерируются данные.
К счастью, мы можем реализовать клиентскую оболочку, которая бы это делала.
Я обновил ваш пример здесь:
<!DOCTYPE html>
<html>
<head>
<title>AmCharts multiple datasets min-max date range issue</title>
<meta charset="utf-8" />
<style type="text/css">
#chartdiv {
width: 100%;
height: 500px;
}
</style>
<script id="data-set-1" type="application/json">
[
{"date": "2016-10-02T10:00:00", "value": 23.8},
{"date": "2016-10-02T10:05:00", "value": 16.8},
{"date": "2016-10-02T10:10:00", "value": 20.5}
]
</script>
<!-- This dataset has max end date from all 3 datasets -->
<script id="data-set-2" type="application/json">
[
{"date": "2016-10-02T10:00:00", "value": 15.2},
{"date": "2016-10-02T10:05:00", "value": 21.4},
{"date": "2016-10-02T10:15:00", "value": 18.1}
]
</script>
<!-- This dataset has min start date from all 3 datasets -->
<script id="data-set-3" type="application/json">
[
{"date": "2016-10-02T09:55:00", "value": 12.4},
{"date": "2016-10-02T10:00:00", "value": 17.7},
{"date": "2016-10-02T10:05:00", "value": 14.6}
]
</script>
<link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />
</head>
<body>
<div id="chartdiv"></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/amstock.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
<script type="text/javascript">
function syncDataTimestamps(chart) {
// check if plugin is enabled
if (chart.syncDataTimestamps !== true)
return;
// go thorugh all data sets and collect all the different timestamps
var dates = {};
for (var i = 0; i < chart.dataSets.length; i++) {
var ds = chart.dataSets[i];
for (var x = 0; x < ds.dataProvider.length; x++) {
var date = ds.dataProvider[x][ds.categoryField];
if ( !(date instanceof Date))
date = new Date(date);
if (dates[date.getTime()] === undefined)
dates[date.getTime()] = {};
dates[date.getTime()][i] = ds.dataProvider[x];
}
}
// iterate through data sets again and fill in the blanks
for (var i = 0; i < chart.dataSets.length; i++) {
var ds = chart.dataSets[i];
var dp = [];
for (var ts in dates) {
if (!dates.hasOwnProperty(ts))
continue;
var row = dates[ts];
if (row[i] === undefined) {
row[i] = {};
var d = new Date();
d.setTime(ts);
row[i][ds.categoryField] = d;
}
dp.push(row[i]);
}
dp.sort(function(a,b){
return new Date(a[ds.categoryField]) - new Date(b[ds.categoryField]);
});
ds.dataProvider = dp;
}
};
$(document).ready(function () {
AmCharts.useUTC = true; // this will prevent adding timezone hours to ech date
var chart = AmCharts.makeChart("chartdiv", {
"type": "stock",
"theme": "light",
"categoryAxesSettings": {
"minPeriod": "mm" // precision to minutes
},
"syncDataTimestamps": true,
"dataSets": [], // empty, we will add each based on single sensor
"panels": [{
"recalculateToPercents": "never", // show value on scale not percent
"showCategoryAxis": true,
"title": "",
"percentHeight": 70,
"stockGraphs": [{ // generic config for all lines
"id": "g1",
"connect": true, // show gaps in data
"comparable": true, // must be true to disable/enable each dataset
"compareField": "value",
"valueField": "value",
"type": "smoothedLine",
"lineThickness": 2,
"bullet": "round"
}],
"stockLegend": {
"periodValueTextRegular": "[[value.close]]" // what will be shown at top legend
}
}],
"chartScrollbarSettings": {
"graph": "g1",
"usePeriod": "10mm",
"position": "top"
},
"chartCursorSettings": {
"valueBalloonsEnabled": true
},
"periodSelector": {
"fromText": "",
"toText": "",
"periodsText": "",
"position": "top",
"dateFormat": "YYYY-MM-DD JJ:NN",
"inputFieldWidth": 150,
"periods": [{
"period": "hh",
"count": 1,
"label": "1 H",
"selected": true
}, {
"period": "hh",
"count": 8,
"label": "8 H"
}, {
"period": "DD",
"count": 1,
"label": "1 D"
}, {
"period": "DD",
"count": 10,
"label": "10 D"
}, {
"period": "MM",
"selected": true,
"count": 1,
"label": "1 M"
}, {
"period": "YYYY",
"count": 1,
"label": "1 Y"
}, {
"period": "YTD",
"label": "YTD"
}, {
"period": "MAX",
"label": "MAX"
}]
},
"panelsSettings": {
"usePrefixes": true
},
"export": {
"enabled": true,
"exportTitles": true,
"libs": {
"path": "http://www.amcharts.com/lib/3/plugins/export/libs/"
},
"position": "bottom-right"
}
});
for (var i = 1; i <= 3; i++) {
var dataset = new AmCharts.DataSet();
dataset.compared = true;
dataset.title = "DataSet " + i;
dataset.categoryField = "date";
dataset.fieldMappings = JSON.parse('[{"fromField": "value", "toField": "value"}]');
var data = JSON.parse($("#data-set-" + i).html());
dataset.dataProvider = data;
chart.dataSets.push(dataset);
}
syncDataTimestamps(chart);
chart.validateData();
});
</script>
</body>
</html>
Обратите внимание, что я также переехал validateData()
Вызов из цикла, поэтому он вызывается только один раз, а не излишне три раза.