Как стать автором
Обновить

Gnuplot на домашней страничке

Время на прочтение5 мин
Количество просмотров20K

Зачем?


При разработке доступной онлайн базы данных для хранения результатов расчётов возникло непреодолимое желание представлять информацию не только в табличном виде, но и в виде графиков. Можно пойти различными путями, например, рисовать кривые в PHP, но правильнее (в смысле UNIX-way) будет использовать внешнюю программу, уже умеющую строить графики, такую как Gnuplot.

Особенно интригует возможность вывода графиков в виде набора JS комманд для рисования на HTML5-холсте (canvas), чем мы и займёмся.

О Gnuplot много писали, в том числе и на Хабре (1, 2), поэтому нет необходимости в подробном описании его синтаксиса.

К своему удивлению, я не обнаружил хорошего туториала о том, как связать вывод gnuplot с динамически формируемой страничкой. Возможно, это связано с недостатком знаний о Java Script и на самом деле всё очевидно, но тем не менее, есть надежда, что эта статья окажется полезной.

Вывод Gnuplot в HTML5 Canvas


Чтобы получить представления о возможностях такого режима, составим минимальный скрипт, рисующий кусок синусоиды в файле output.html:

#!/usr/bin/gnuplot
set terminal canvas
set output "output.html"
plot sin(x) with lines

Здесь первая строка задает интерпретатор команд, вторая — устанавливает формат вывода canvas. Для особо удачных графиков здесь можно было бы заменить canvas на pdf и сразу вставить результат в статью для, скажем, Nature. Третья строка указывает имя файла для записи, если ее не будет — то поток будет направлен в stdout, чем мы воспользуемся при генерации html-странички. Последняя строка содержит собственно команду для построения графика синуса.

Открыв сгенерированный файл output.html, можно увидеть график:



Хорошо, но такой результат вполне можно было бы получить, вставляя рисунки, полученные и терминалом png, а хотелось бы интерактивности! Для этого нужно всего лишь указать параметры терминала enhanced mousing:

set terminal canvas enhanced mousing 
set output "output.html"
set xlabel 'Time'
set ylabel 'Energy'
plot sin(4*x)/x with lines linewidth 2

В результате появится панелька с управляющими кнопками, возможность ставить точки и приближать интересующую область:



Уже больше похоже на примеры с сайта разработчиков, но с удаленного компьютера работать не будет, поскольку за вспомогательными JS-скриптами идет обращение в локальную файловую систему, так как сгенерированный файл содержит строки типа:

<script src="/usr/share/gnuplot/gnuplot/4.6/js/gnuplot_common.js"></script>

То есть, нужно разместить файлы из /usr/share/gnuplot/gnuplot/4.6/js на своем сайте, скопировав их или создав ссылку. Для работы достаточно иметь файлы canvastext.js, gnuplot_common.js, gnuplot_mouse.js, gnuplot_mouse.css и png-иконки. Также необходимо указать путь к этим файлам в параметрах терминала:

set terminal canvas enhanced mousing jsdir 'js'


Взаимодействие PHP и gnuplot


Итак, есть путь к папке расчётов, нужно построить график согласно выбранным пунктам на html-страничке.
Разобьем задачу на несколько частей, как показано на диаграмме:



Такая схема, возможно, избыточна, и всю функциональность можно реализовать на php, но такое разделение разбивает код по его функциям и правам доступа:
  • shell-скрипт имеет доступ к файлам на диске и запуску gnuplot, но не должен быть доступен снаружи,
  • php и html — наоборот, доступны снаружи и не должны иметь доступа к файлам за пределами сайта,
  • отдельный .gp файл позволяет повторить построение графика, но в более удобном для публикации формате выбором pdf или png терминалов.


PHP/HTML


html-форма и php-скрипт объединены в один файл plot_calc.php:

<?php
// получаем и устанавливаем управляющие переменные:
$path = $_GET['path'];                        // user-friendly путь,
$fullpath = "/srv/calculations/".$path."/";   // полный путь в локальной ФС
$nSites = $_GET['nsites'];                    // параметр расчета - число 'сайтов'

$fSite = 1;                                   // текущий 'сайт'
if (isset($_POST["fSite"])) { $fSite = $_POST["fSite"]; }

// опущено описание заголовка и стилей
echo '<a href=index.html>Back</a>';
echo '<h1>Plot Calculation Data</h1>';
echo '<form id="form_plots" method="post" action=plot_calc.php?path='.$path.'&nsites='.$nSites.'>';
echo '    <input id="plotProb"  type="submit" name="plotProb" value="Prob" />';
echo '    <input id="plotEnergy" type="submit" name="plotEnergy" value="Energy" />';
// ...
echo '    <label>Site number to plot</label>';
echo '    <input id="fSite" name="fSite"  type="text" value='.$fSite.' />';
echo '</form>';

if (isset($_POST["plotProb"])) {   // если нажата кнопка plotProb
        $input='./plot_calc.sh'.' '.'p '.$nSites.' '.$fullpath.' '.$fSite;
        echo "Probability plot for Site #".$fSite;
    }
if (isset($_POST["plotEnergy"])) { // если нажата кнопка plotEnergy
        $input='./plot_calc.sh'.' '.'E '.$nSites.' '.$fullpath.' '.$fSite;
        echo "Energy plot for Site #".$fSite;
    }
// ... 

$output = shell_exec($input); // запуск скрипта оболочки
echo $output;                 // вывод результата работы скрипта

echo '</body>';
echo '</html>';
?>

Как заметили в комментариях, вызов shell_exec в таком виде является опасной затеей, так что следует, как минимум, провести фильтрацию данных с помощью функции filter_input.

SHELL


Задача скрипта plot_calc.sh — формирование команд для gnuplot согласно заданным внешним параметрам. Скрипт создает файл во временной директории /tmp, содержимое которого передается gnuplot, а результат выполнения возвращается обратно, где его уже ждет php-скрипт.

#!/bin/bash
# параметры:
# 1 - тип
# 2 - число сайтов
# 3 - путь
# 4 - сайт для рисования (требуется для p)
#  --------------
# тип   значение   
# E	energy
# p     probaility
# ....
#------------------------------#

# создадим новый файл во временном каталоге:
TFILE="/tmp/$(basename $0).$$.gp"
# запишем в него команды gnuplot:
echo "# Automatically generated Gnuplot script " > $TFILE
echo "set terminal canvas enhanced mousing jsdir 'js'" >> $TFILE

### Probability ###
if [ $1 == 'p' ]
then
    echo "set xlabel 'Time'">>$TFILE
    echo "set ylabel 'Probability'">>$TFILE
    let icol=$4+1
    echo "plot '$3prob.res' u 1:$icol wi li">>$TFILE
fi

# ...

### Energy ###
if [ $1 == 'E' ]
then
    let col=$4*2
    let col1=$4*2+1
    echo "set xlabel 'Time'">>$TFILE
    echo "set ylabel 'Energy'">>$TFILE
    echo "plot '$3Energ.res' u 1:$col:$col1 wi err">>$TFILE
fi

/usr/bin/gnuplot $TFILE

Этому файлу потребуются права на исполнение:
 chmod +x plot_calc.sh 

Gnuplot


Скрипт оболочки формирует выходной файл в имени которого содержится имя вызвавшего скрипта и случайное число, внутренность у него примерно такая:

# Automatically generated Gnuplot script 
set terminal canvas enhanced mousing jsdir 'js'
set xlabel 'Time'
set ylabel 'Probability'
plot '/srv/calculations/GoodCalc/prob.res' u 1:6 wi li

Проверить работу можно направив браузер по адресу http://servername/plot_calc.php?path=GoodCalc&nsites=10.
В результате появится возможность построить графики по данным файлов prob.res и Energy.res, и должна получиться страничка, похожая на:



Генерируемые файлы в /tmp надо время от времени подчищать, для этого сгодится задание cron (от root'а):

crontab -e

0 */1 * * * rm -v /tmp/plot*.gp >> /var/log/rmplotgp.log


Заключение


  1. Тестирование и демонстрация проводились на Debian Jessie, но должно работать и на прочих системах;
  2. JS-файлы gnuplot должны быть доступны из внешней сети;
  3. Есть возможность формирования не только html-страничек, но и js-скриптов, управляемых пользовательским кодом.
  4. Вопросы нагрузки и безопасности здесь не рассматривались и потребуют дополнительного внимания...
Теги:
Хабы:
Всего голосов 21: ↑18 и ↓3+15
Комментарии12

Публикации

Истории

Ближайшие события

19 августа – 20 октября
RuCode.Финал. Чемпионат по алгоритмическому программированию и ИИ
МоскваНижний НовгородЕкатеринбургСтавропольНовосибрискКалининградПермьВладивостокЧитаКраснорскТомскИжевскПетрозаводскКазаньКурскТюменьВолгоградУфаМурманскБишкекСочиУльяновскСаратовИркутскДолгопрудныйОнлайн
24 – 25 октября
One Day Offer для AQA Engineer и Developers
Онлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
26 октября
ProIT Network Fest
Санкт-Петербург
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань