Gnuplot пакуем выходной svg в один файл
Старый мем на новый лад.
Предыдущая моя статья «Gnuplot и с чем его едят» получила большой отклик и даже была переведена на несколько языков (видел на медиуме, встречал на немецком). Поэтому, раз тема актуальная, решил продолжить.
У меня появилась задача получать данные, а затем на удалённом сервере строить по ним графики и отправлять по почте. Причём графики должны иметь возможность отключать оси, приближать отдельные области графика, включать-выключать сетку. И вы знаете, gnuplot умеет выдавать подобные графики. Он даёт их в формате html или svg. Но вот незадача, вместе с этим файлом надо тащить ещё багаж данных в виде кучи javascript-файликов, картинок, css (в случае html), что сильно сужает применимость при отправке их по почте.
В результате, нашёл-таки решение данной проблемы и продемонстрирую её решение на примере svg-файла, для html будет аналогично. Поскольку нет возможности привести график реальных данных, где было использовано это решение, в пример взял шуточный мем про студентов
Постановка задачи
В gnuplot есть прекрасная возможность генерировать графики в html или svg. Они получаются интерактивными и их можно встраивать в ваши веб-страницы или в файлы отчётов. Существует очень хорошая статья на хабре по данной теме: Gnuplot на домашней страничке. Где весьма подробно было разобрано, как формировать данные в html. Посмотреть, как будут выглядеть подобные графики вживую можно на демонстрационной странице для svg и для html. Рекомендую попробовать пощёлкать мышью, повыделять правой кнопкой мыши области графика, включить-выключить сетку и т.п. Сразу станут ясны возможности.
В чём же собственно говоря кроется проблема? Проблема достаточно простая, для работы svg и html нужны дополнительные файлы. То есть, мы формируем отчёт в красивом файле, не глядя его отправляем, а на том конце он не работает и вместо картинок крестики.
Проще говоря, если открыть полученный html, то там можно будет увидеть следующие строки:
Скрипты, которые лежат по прямому пути, и не содержатся в результирующем html-файле.
То же самое с разметкой css:
Картинки тоже лежат «где-то», и как вы понимаете они не копируются вместе с файлом.
')
elif 'grid.png' in string:
start = string.find(IMG_STR_START) + len(IMG_STR_START)
end = string.find(IMG_STR_END) + len(IMG_STR_END)
img_in_base64 = '''
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAADFBMVEX///8AAAAAAAAAAAD4jAJN
AAAAA3RSTlMA7Psi0lOqAAAAHElEQVR4AWPABpjgCLsAExMzAjEOMS2MCIQFAABtJADJXH3sOwAA
AABJRU5ErkJggg=='''
svg_out.append(string[0:start] +
'data:image/png;charset=utf-8;base64, ' +
img_in_base64 + "'" + string[end:])
else:
svg_out.append(string)
with open('output.svg', 'w') as f:
f.write('\n'.join(svg_out))
Вначале идут паттерны поиска джаваскрипта и картинки, далее открываем svg-файл и превращаем его в список строк и по этому списку строк бежим. Если у нас не попадает в паттерн, то мы сохраняем строку в новый список, а если попадает, то модифицируем строку и добавляем её в новый список.
После всего сохраняем этот список в файл (можно даже формировать новый).
▍ Функция main
Всё просто:
- Получаем исходные данные
- Формируем метки по Y
- Сохраняем данные в промежуточный файл
- Строим график
- Обновляем svg
def main():
data_for_plot = get_remote_data()
ylabel = set_ytics_label()
datafile = create_file_to_plot(data_for_plot)
title = 'Уровень знаний в течение семестра'
plot_graph(datafile, ylabel, title)
os.unlink(datafile)
update_svg()
Вывод
Для меня было некоторым открытием, что такой известный, старейший инструмент, как gnuplot, который делает потрясающие графики, имеет столько недочётов и требуется делать вот такие вот костыли, чтобы формировать качественный выходной файл. То что не поддерживается кириллица (да и вообще unicode), в html выходном файле — это вообще печаль. Зато это хороший повод вписать своё имя в историю великого пакета. Всё это справедливо для пакета: Version 5.0 patchlevel 3 last modified 2016–02–21. Возможно на данный момент эти проблемы решены.
Касательно html, код выглядит практически также, разве что картинок больше, и их удобнее не хардкодить в тексте, а преобразовывать на лету в base64:
import base64
with open(pngfilename) as pngfile:
base64img = base64.b64encode(pngfile.read())
html_new.append(string[0:start] + 'data:image/png;base64, '
+ base64img.decode("utf-8) + string[end:])
Ну и следует помнить, что там ещё есть css и несколько js-файлов.
Умышленно не привожу целиком код для html, чтобы был повод размять мозги.
Код этого проекта обитает вот тут: github.com/dlinyj/gnuplot_svg_update
Попробую вставить svg-картинку на хабр с гита, посмотрю что получится.
Конечно, скрипты на хабре не работают (и это правильно), но картинка грузится.
Меня могут попрекнуть, мол подобрал бы инструмент, с которым бы не было таких проблем. Всё это хорошо, когда ты админ на машине. А когда у тебя есть суровый питон, запрет на доустановку пакетов и gnuplot, то приходится выкручиваться.
P/s есть ещё парочка хостингов svg, тут и тут (надо регаться).