Строим графики
Строим графики
Предлагаю в этой теме обсуждать библиотеки для построения графиков, описывать примеры, делиться опытом и помогать с возникшими проблемами.
На данный момент мне известны следующие библиотеки:
1. JpGraph
2. flot
3. amCharts
4. pChart
5. jqPlot
6. Highcharts
7. dygraphs
8. Flotr2
9. Envision
10. D3.js
Я пробовал строить графики только с помощью библиотеки flot. В принципе flot хорошая библиотека, но у меня возникли некоторые трудности в настройке под себя, которые я опишу чуть позже.
Библиотеки JpGraph и pChart похожи тем, что создают графический объект, т.е. график выглядит в виде каритнки. Библиотеки amCharts, jqPlot, Highcharts, dygraphs и flot используют jQuery и HTML5, что даёт им большую интерактивность и функциональность.
На данный момент мне известны следующие библиотеки:
1. JpGraph
2. flot
3. amCharts
4. pChart
5. jqPlot
6. Highcharts
7. dygraphs
8. Flotr2
9. Envision
10. D3.js
Я пробовал строить графики только с помощью библиотеки flot. В принципе flot хорошая библиотека, но у меня возникли некоторые трудности в настройке под себя, которые я опишу чуть позже.
Библиотеки JpGraph и pChart похожи тем, что создают графический объект, т.е. график выглядит в виде каритнки. Библиотеки amCharts, jqPlot, Highcharts, dygraphs и flot используют jQuery и HTML5, что даёт им большую интерактивность и функциональность.
Последний раз редактировалось Aquarius 07 янв 2015, 17:02, всего редактировалось 4 раза.
Re: Строим графики
Я ипользую flot
Re: Строим графики
А никаких плюшечек (зуммирование, подсказки с показаниями где мышка стоит) не используете?Boris писал(а):Я ипользую flot
Re: Строим графики
НетAquarius писал(а):А никаких плюшечек (зуммирование, подсказки с показаниями где мышка стоит) не используете?Boris писал(а):Я ипользую flot
Re: Строим графики
А у Вас не было проблем со временем? То есть из базы у Вас данные передаются, например 25 апреля 2012 года в 12:00 была температура 25 градусов, а на графике шкала показывает время 8:00. Т.е. с часовыми поясами я так понял какая-то несостыковка.Boris писал(а):Нет
Отсюда вопросы:
1. Как Вы храните время в базе - MySQL TIMESTAMP (2012-04-25 12:32:01) или UNIX-TIMESTAMP (1334663403)?
2. Как добавляете значение в базу, т.е. как формируете время?
Re: Строим графики
Пример построения графика с помощью библиотеки flot.
Для того, чтобы построить график необходимо данные. Чтобы сформировать данные я написал такой РНР-скрипт:
Таблица состоит из 4-х столбцов: индекс, дата/время, номер датчика, значение. Данные возвращаются закодированные в JSON формат.
После этого нам нужно написать HTML страничку, на которой будет находиться наш график:
Я воспользовался уже имевшимся файлом в примерах, только переделал его, добавив зуммирование, несколько осей и хотел, чтобы при передвежении мышки отображалось в легенде значение, но не получилось. Не хочет выполняться этот "legends.eq(i).text(series.label + ": " + series.data[j][1]);" кусок и всё, хотя в примере работает нормально. Думаю попробовать сдлать тогда всплывающие тултипы, но пока руки не дошли.
Так же были проблемы со временем, а точнее с часовыми поясами. В график загружаю данные, а на оси Х время отображается некорректно. Пришлось подредактировать файл jquery.flot.js и добавить в PHP-скрипт коррекцию.
Результат работы можно посмотреть здесь.
Если у кого-то были такие же проблемы, как и у меня, поделитесь, пожалуйста, методами их решения.
Для того, чтобы построить график необходимо данные. Чтобы сформировать данные я написал такой РНР-скрипт:
Код: Выделить всё
<?PHP
ini_set('display_errors',1);
error_reporting(E_ALL);
setlocale(LC_ALL, 'ru_RU');
//date_default_timezone_set('Europe/Moscow');
//---------------------------------------------------------------
$fd = "2012-04-01 00:00:00";//параметр с даты
$td = "2200-01-01 00:00:00";//параметр по дату
foreach($_GET as $key => $value)
{
$$key = $value;
}
if (isset($sensor)) //если не передан параметр датчика, то ничего не выводится
{
include "config.php";//файл с параметрами подключеня к БД
if (isset($_GET['fd']) || isset($_GET['td']))//если передали параметры дат, то формируется дополнительные параметры запроса
{
$func = " AND time BETWEEN STR_TO_DATE('".$fd."', '%Y-%m-%d %H:%i:%s') AND STR_TO_DATE('".$td."', '%Y-%m-%d %H:%i:%s')";
}
else $func = "";
$timecorrect = "-28800000";//поправка на часовой пояс для построения графиков
$request = "SELECT UNIX_TIMESTAMP(time)*1000".$timecorrect.", value, (SELECT name
FROM sensor
WHERE sensor=id)
FROM weather
WHERE sensor='".$sensor."'".$func.";";//формируем запрос из которого получаем дату и значение
$query = mysql_query($request);//выполняем запрос
if($query)
{
if (mysql_num_rows($query) > 0) //если запрос вернул 0 записей, то ничего не выводится
{
$src = array();
while ($res = mysql_fetch_array($query))
{
$src[] = array((float) $res[0],(float) $res[1]);//добавляем в массив
$label = $res[2];//добавляем название датчика
}
$graph = array(
'data' => $src
, 'label' => $label
);//объединили данные и название в один массив
if (isset($yaxis)) //если в запросе указано к какой оси Y относится график - добавляем в массив этот праметр
{
$graph += array('yaxis' => (int) $yaxis);
}
if (isset($xaxis)) //если в запросе указано к какой оси X относится график - добавляем в массив этот праметр
{
$graph += array('yaxis' => (int) $xaxis);
}
echo json_encode($graph);//кодируем массив в формат JSON и выводим его
//echo gettype($axis);
//print_r($graph);
}
mysql_free_result($query);
}
else
{
echo("Error: ".mysql_error());
exit();
}
if(!mysql_close($dbcnx)) // разрываем соединение
{
echo("Не удалось завершить соединение");
}
}
exit();
?>
После этого нам нужно написать HTML страничку, на которой будет находиться наш график:
Код: Выделить всё
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Flot Examples</title>
<link href="layout.css" rel="stylesheet" type="text/css">
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="javascript/flot/excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="javascript/flot/jquery.js"></script>
<script language="javascript" type="text/javascript" src="javascript/flot/jquery.flot.js"></script>
<script language="javascript" type="text/javascript" src="javascript/flot/jquery.flot.selection.js"></script>
<script language="javascript" type="text/javascript" src="javascript/flot/jquery.flot.crosshair.js"></script>
</head>
<body>
<h1>Flot Examples</h1>
<div id="placeholder" style="width:600px;height:300px;"></div>
<div id="overview" style="margin-top:20px;width:600px;height:50px"></div>
<p>You selected: <span id="selection"></span></p>
<p id="hoverdata">Mouse hovers at
(<span id="x">0</span>, <span id="y">0</span>). <span id="clickdata"></span></p>
<p>Time:<span id="time"></span></p>
<p><span id="clickdata0"></span></p>
<p><span id="clickdata1"></span></p>
<script type="text/javascript">
var plot;
$(function () {
var options = {
//lines: { show: true },
//points: { show: true },
selection: { mode: "x" },
crosshair: { mode: "x" },
grid: { hoverable: true, autoHighlight: false },
xaxes: [ { mode: "time" }
],
yaxes: [
{ tickFormatter: tickCelsius
//,minTickSize: 1
},
{ position: "right"
,minTickSize: 1
,tickFormatter: tickHumidity
//show: true
}
]
};
var optionsoverview = {
series: {
lines: { show: true, lineWidth: 1 },
shadowSize: 0
},
xaxis: { ticks: [], mode: "time" },
yaxis: { ticks: [] },
selection: { mode: "x" },
legend: {show: false}
};
function tickHumidity(val) {
return val.toFixed(0) + "%";
}
function tickCelsius(val) {
return val.toFixed(1) + "°";
}
var src = [];
var plot = $.plot($("#placeholder"), src, options);
var overview = $.plot($("#overview"), src, optionsoverview);
function onDataReceived(series) {
src.push(series);
plot = $.plot($("#placeholder"), src, options);
overview = $.plot($("#overview"), src, optionsoverview);
}
$.get('datasrc.php', { sensor: "1", yaxis: "1" }, onDataReceived, "json");
$.get('datasrc.php', { sensor: "2", yaxis: "2" }, onDataReceived, "json");
function checkZero(i)
{
if (i < 10)
{
i= "0" + i;
}
return i;
}
var legends = $("#placeholder .legendLabel");
legends.each(function () {
// fix the widths so they don't jump around
$(this).css('width', $(this).width());
});
var updateLegendTimeout = null;
var latestPosition = null;
function updateLegend() {
updateLegendTimeout = null;
var pos = latestPosition;
var i, j, dataset = plot.getData();
for (i = 0; i < dataset.length; ++i) {
var series = dataset[i];
for (j = 0; j < series.data.length; ++j)
if (series.data[j][0] > pos)
break;
var timestamp = new Date(series.data[j][0]);
var year = timestamp.getFullYear();
var month = timestamp.getMonth() + 1;
month = checkZero(month);
var day = timestamp.getDate();
day = checkZero(day);
var hour = timestamp.getHours();
hour = checkZero(hour);
var minutes = timestamp.getMinutes();
minutes = checkZero(minutes);
var time = year + "-" + month + "-" + day + " " + hour + ":" + minutes;
$("#time").text(time);
$("#clickdata"+i).text(series.label + ": " + series.data[j][1]);
legends.eq(i).text(series.label + ": " + series.data[j][1]);
//$("#clickdata").text(legends);
}
}
$("#placeholder").bind("plothover", function (event, pos, item) {
$("#x").text(pos.x.toFixed(0));
$("#y").text(pos.y.toFixed(2));
latestPosition = pos.x.toFixed(0);
if (!updateLegendTimeout)
updateLegendTimeout = setTimeout(updateLegend, 50);
});
$("#placeholder").bind("plotselected", function (event, ranges) {
// do the zooming
$("#selection").text(ranges.xaxis.from.toFixed(1) + " to " + ranges.xaxis.to.toFixed(1));
plot = $.plot($("#placeholder"), src,
$.extend(true, {}, options, {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
}));
// don't fire event on the overview to prevent eternal loop
overview.setSelection(ranges, true);
});
$("#overview").bind("plotselected", function (event, ranges) {
plot.setSelection(ranges);
});
});
</script>
</body>
</html>
Так же были проблемы со временем, а точнее с часовыми поясами. В график загружаю данные, а на оси Х время отображается некорректно. Пришлось подредактировать файл jquery.flot.js и добавить в PHP-скрипт коррекцию.
Результат работы можно посмотреть здесь.
Если у кого-то были такие же проблемы, как и у меня, поделитесь, пожалуйста, методами их решения.
Re: Строим графики
Для построения графиков использую rrdtool.
База цикличная. Данные хранятся за последний месяц.
База цикличная. Данные хранятся за последний месяц.
- Вложения
-
- HDD-TEMP_1d.png (44.72 КБ) 30240 просмотров
"Та нам з тобою своє робити, відкрити очі і далі йти!
І зуби сильно стиснувши, маму ніжно любити, хто ж тоді, як не ми, брати?!"(с)
І зуби сильно стиснувши, маму ніжно любити, хто ж тоді, як не ми, брати?!"(с)
Re: Строим графики
Да проблемы с временем были.Aquarius писал(а):А у Вас не было проблем со временем? То есть из базы у Вас данные передаются, например 25 апреля 2012 года в 12:00 была температура 25 градусов, а на графике шкала показывает время 8:00. Т.е. с часовыми поясами я так понял какая-то несостыковка.Boris писал(а):Нет
Отсюда вопросы:
1. Как Вы храните время в базе - MySQL TIMESTAMP (2012-04-25 12:32:01) или UNIX-TIMESTAMP (1334663403)?
2. Как добавляете значение в базу, т.е. как формируете время?
Пишу в базу на С вот так "INSERT INTO weather(node, t, v, dat) VALUES(?, ?, ?, datetime('NOW','localtime'))"
В getdata.php такой текст
Код: Выделить всё
<?php
$hostname = "127.0.0.1";
$username = "user";
$password = "password";
$dbName = "weather";
$dataset1 = array();
$dataset2 = array();
$dataset3 = array();
$dataset4 = array();
$mergedData = array();
class MyDB extends SQLite3
{
function __construct()
{
$this->open('/opt/mydb.db');
}
}
$db = new MyDB();
$data = $db->query('SELECT strftime("%s", dat), t FROM weather where node=22 and rowid%5=0 and dat>datetime("now","localtime","-1 day");');
while ($row=$data->fetchArray()) {
$dataset1[] = array( 1000.00*$row ['strftime("%s", dat)'], $row[t] );
}
$mergedData[] = array('label' => "Outside" , 'data' => $dataset1, 'color' => '#6bcadb');
$data2 = $db->query('SELECT strftime("%s", dat), t FROM weather where node=10 and rowid%5=0 and dat>datetime("now","localtime","-1 day");');
while ($row=$data2->fetchArray()) {
$dataset2[] = array( 1000.00*$row ['strftime("%s", dat)'], $row[t] );
}
$mergedData[] = array('label' => "Inside1" , 'data' => $dataset2, 'color' => '#FF0000');
$data3 = $db->query('SELECT strftime("%s", dat), t FROM weather where node=11 and rowid%5=0 and dat>datetime("now","localtime","-1 day");');
while ($row=$data3->fetchArray()) {
$dataset3[] = array( 1000.00*$row ['strftime("%s", dat)'], $row[t] );
}
$mergedData[] = array('label' => "Inside2" , 'data' => $dataset3, 'color' => '#00EE00');
$data4 = $db->query('SELECT strftime("%s", dat), t FROM weather where node=1 and rowid%5=0 and dat>datetime("now","localtime","-1 day");');
while ($row=$data4->fetchArray()) {
$dataset4[] = array( 1000.00*$row ['strftime("%s", dat)'], $row[t] );
}
$mergedData[] = array('label' => "Inside3" , 'data' => $dataset4, 'color' => '#55FACC');
echo json_encode($mergedData);
?>
Код: Выделить всё
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Flot Examples</title>
<link href="layout.css" rel="stylesheet" type="text/css">
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="../excanvas.min.js"></script><![endif]-->
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
</head>
<body>
<h1></h1>
<p>Temperatura C</p>
<div id="placeholder" style="width:800px;height:600px;"></div>
<script type="text/javascript">
$(function(){
$.ajax({
url: 'getdata.php',
dataType: 'json',
success:function(series){
var length = series.length;
var finalData = series;
var options = {
xaxis: {
mode: "time",
localTimezone: true,
timeformat: "%h:%M"
},
yaxis: {
position: "right",
tickSize: 1,
},
lines: { show: true },
legend: {position: "nw"},
};
$.plot($("#placeholder"), finalData, options);
}
});
});
</script>
</body>
</html>
Re: Строим графики
у себя в проекте использую pChart -- другие не пробовал, но это работает -- просто, надёжно, вполне себе для глаз приемлемо
Re: Строим графики
Мне dygraphs симпатичен "рюшами" типа зуммирования и прокруток.Aquarius писал(а):Предлагаю в этой теме обсуждать библиотеки для построения графиков, описывать примеры, делиться опытом и помогать с возникшими проблемами.
...
7. dygraphs
Re: Строим графики
вопрос пользователям flot, вы используете какуюто спец корнвертацию данных из БД в JSON файл? а также, что будет если написать скрипт который будет переодически пополнять файл JSON данными которые при надобности будет отображаться в виде графика, есть ли ограничения на размер файла JSON, зарание прошу иметь ввиду то что график будет выполнять ресурсами роутера, а также дайте пожалуйста совет может мне применить какойто определенный тип графиков (облегченный, легкий) из представленного вначале темы списка
Re: Строим графики
В РНР есть специальная функция, которая конвертирует данные из массива (такой тип данных РНР) в формат JSON: echo json_encode($graph);Dmitry K писал(а):вопрос пользователям flot, вы используете какуюто спец корнвертацию данных из БД в JSON файл? а также, что будет если написать скрипт который будет переодически пополнять файл JSON данными которые при надобности будет отображаться в виде графика, есть ли ограничения на размер файла JSON, зарание прошу иметь ввиду то что график будет выполнять ресурсами роутера
Файл JSON пополнять не нужно, нужно просто запомнить дату/время последней точки и затем уже подгружать данные только с этой точки и добавлять в массив, который уже загружен - с этим я буду разбираться в будущем (автоматическое обновление графика).
Про ограничения сказать ничего не могу, а про роутер я уже писал, что у меня он зависал, но это возможно связано с MySQL Server-ом...
Re: Строим графики
по этому я и бьюсь в поисках легкой БД сейчас присматриваюсь к mysqlite - возможно некоторые подумают "бестолковый не как не поймет что это не для роутера" скажу сразу у меня некий своеобразный фетиш ограничиться ресурсами роутера
п.с. какой из этих инстументов постройки графиков:
1. JpGraph
2. flot
3. amCharts
4. pChart
5. jqPlot
6. Highcharts
7. dygraphs
самый легкий относительно ресурсов а также легкий в освоении?
п.с. какой из этих инстументов постройки графиков:
1. JpGraph
2. flot
3. amCharts
4. pChart
5. jqPlot
6. Highcharts
7. dygraphs
самый легкий относительно ресурсов а также легкий в освоении?
Re: Строим графики
Расчёт графиков ложиться на ресурсы клиентского компьютера у всех библиотек, кроме JpGraphи pChart.Dmitry K писал(а): самый легкий относительно ресурсов а также легкий в освоении?
Я пока освоил только flot и мне он не очень понравился, поэтому в ближайшее время буду пробовать другие и отписываться сюда.
Re: Строим графики
что касаемо графиков, держите меня в курсе я тоже начал подключать flot для мониторинга температурыAquarius писал(а):В РНР есть специальная функция, которая конвертирует данные из массива (такой тип данных РНР) в формат JSON: echo json_encode($graph);Dmitry K писал(а):вопрос пользователям flot, вы используете какуюто спец корнвертацию данных из БД в JSON файл? а также, что будет если написать скрипт который будет переодически пополнять файл JSON данными которые при надобности будет отображаться в виде графика, есть ли ограничения на размер файла JSON, зарание прошу иметь ввиду то что график будет выполнять ресурсами роутера
Файл JSON пополнять не нужно, нужно просто запомнить дату/время последней точки и затем уже подгружать данные только с этой точки и добавлять в массив, который уже загружен - с этим я буду разбираться в будущем (автоматическое обновление графика).
Про ограничения сказать ничего не могу, а про роутер я уже писал, что у меня он зависал, но это возможно связано с MySQL Server-ом...
Re: Строим графики
набрасал тут небольшой пример в голове, что если зделать так: написать скрипт который будет подключаться к физической среде например как у меня контроллер + dsки опрашивать их затем создавать json файл и писать в него данные с датчиков и время, но делать это совеобразно к примеру каждый день новый файл начиная в 00:00 и заканчивая 23:00 обзывая файл точной датой например 02_05_2012.json и записывая туда 12 значений за сутки к примеру при замерах каждый час (тут каму как по вкусу), и так каждый день, пользователь сможет сам выбрать и просмтреть график температуры за определенный деть? а также сможет сравнивать температуру путем налажения данных любого дня + зделать так чтобы была автоматическая подчистка файлов например каждый месяц, т.к больше чем за месяц температуру смотреть нет смысла (мы ведь не гидромицентр) и того получится 30-31 файл за месяц + 30-31 за прошлый для сравнения и как только файлов станет 90 удалить самые последние 30 получим сводку за текущий месяц + предыдущий с автоматической чисткой позопрошлого
Re: Строим графики
набрасал тут небольшой пример в голове, что если зделать так: написать скрипт который будет подключаться к физической среде например как у меня контроллер + dsки опрашивать их затем создавать json файл и писать в него данные с датчиков и время, но делать это совеобразно к примеру каждый день новый файл начиная в 00:00 и заканчивая 23:00 обзывая файл точной датой например 02_05_2012.json и записывая туда 12 значений за сутки к примеру при замерах каждый час (тут каму как по вкусу), и так каждый день, пользователь сможет сам выбрать и просмтреть график температуры за определенный деть? а также сможет сравнивать температуру путем налажения данных любого дня + зделать так чтобы была автоматическая подчистка файлов например каждый месяц, т.к больше чем за месяц температуру смотреть нет смысла (мы ведь не гидромицентр) и того получится 30-31 файл за месяц + 30-31 за прошлый для сравнения и как только файлов станет 90 удалить самые последние 30 получим сводку за текущий месяц + предыдущий с автоматической чисткой позопрошлого получится своебразное накопление статистики в обход БД тем самым не нагружаю оборудование например как я терзаю свой роутер
Re: Строим графики
Сомневаюсь, что тем самым Вы не будете терзать свой роутер. Ему так или иначе придётся читать все эти файлы, а если учесть, что их у вас будет штук 30, то это мне кажется будет ощутимо для него.Dmitry K писал(а):получится своебразное накопление статистики в обход БД тем самым не нагружаю оборудование например как я терзаю свой роутер
Мне кажется не надо ничего выдумывать, а попробовать использовать sqlite. С помощью sqlite намного удобнее делать выборки по дате/времени, а с файлами надо будет ещё возиться чтобы их все перебрать. И не надо ни чего хранить в JSON и что-то в него добавлять.
Я уже писал пример как добавлять данные в БД - переделайте пример под sqlite и всё. Чтобы обращаться к скрипту добавления значений в БД также я приводил скрипт считывания данных. Всё уже есть, Вам нужно только написать скетч для ардуины, чтобы она при получении "1" отправляла следующее: ds[0]=<адрес датчика>&val[0]=<значение>&ds[1]=<адрес датчика>&val[1]=<значение>, изменить мой пример, который работает с MySQL Server на sqlite.
Скрипт получает строку, распарсивает, в цикле перебирает массив ds и добавляет в БД.
Re: Строим графики
буду пробовать но тут дело в другом, у меня два ds1821, которые не имеют адресации и в порт выдают только температуру, в целых числах без десятых, тк как точность у них до градуса не более а следовательно придется делать так ds[1]&val[1]=<значение>&ds[2]&val[2]=<значение> верно? п.с. что означет "&" просто скрепляет строку чтобы она была целой? при последующей переработки (распарсивание) просто игнорируется? дайте ссылок как догоняли чтобы все это завернуть?
как у вас происходит процедура выборки? а именно к примеру последние 10 переменных отдают во flot?
как у вас происходит процедура выборки? а именно к примеру последние 10 переменных отдают во flot?
Re: Строим графики
Если адресов нет, то можно ds[0]=1&val[0]=<значение>&ds[1]=2&val[1]=<значение> - массивы нумеруются с нуля. Знак "&" соединяет значения. Если Вы посмотрите в адресную строку своего браузера, находясь, например на этом форуме, то увидите те же символы. Вот здесь можно почитать про функцию распарсивания строки, т.е. из строки выше мы получим два массива: ds(1,2) и val(<значение 1>, <значение 2>). На этом же сайте очень много информации по РНР с хорошими примерами.Dmitry K писал(а):буду пробовать но тут дело в другом, у меня два ds1821, которые не имеют адресации и в порт выдают только температуру, в целых числах без десятых, тк как точность у них до градуса не более а следовательно придется делать так ds[1]&val[1]=<значение>&ds[2]&val[2]=<значение> верно? п.с. что означет "&" просто скрепляет строку чтобы она была целой? при последующей переработки (распарсивание) просто игнорируется? дайте ссылок как догоняли чтобы все это завернуть?
как у вас происходит процедура выборки? а именно к примеру последние 10 переменных отдают во flot?
Не понял вопроса про выборку и передачу во flot.