% -*- mode: Noweb; noweb-code-mode: c-mode -*- The plotly structure is used to display CEO graphics using \url{plot.ly}. \section{The files} \label{sec:files} \subsection{Header} \label{sec:header} <>= #ifndef __PLOTLY_H__ #define __PLOTLY_H__ #ifndef __PTHREAD_H__ #include #endif #ifndef __CURL_H__ #include #endif #ifndef __UTILITIES_H__ #include "utilities.h" #endif #ifndef __JSMN_H__ #include "jsmn.h" #endif #include "plotly.credentials" #define P_LEN 64 struct plotly_properties{ <> <> int set(const char *param, const char *value); int set(const char *param, float *value, int N); int set(const char *param, float *value, int N, int M); }; void imagesc(plotly_properties *p); void plot(plotly_properties *p); struct plotly_resp { char url[256]; char message[256]; char warning[256]; char filename[256]; char error[256]; }; int x2json(char *zData, float *zdata, int N); int y2json(char *zData, float *zdata, int N); int z2json(char *zData, float *zdata, int N, int M); void curlplotly(void *json); size_t write_data(void *ptr, size_t size, size_t count, void *stream); #endif // __PLOTLY_H__ @ \subsection{Source} \label{sec:source} <>= #include "plotly.h" <> <> <> <> <> <> <> <> <> <> @ \section{Parameters} \label{sec:parameters} The [[plotly_properties]] structure gathers the property of the graph.The properties are: \begin{itemize} \item the graph file name used when exporting the file <>= char filename[P_LEN]; @ \item the graph type: \emph{scatter} for a plot, \emph{heatmap} for an image <>= char type[P_LEN]; @ \item the title of the graph <>= char title[P_LEN]; @ \item the X axis data <>= char *xData; @ \item the label of the X axis <>= char xtitle[P_LEN]; @ \item the Y axis data <>= char *yData; @ \item the label of the Y axis <>= char ytitle[P_LEN]; @ \item the Z axis data <>= char *zData; @ \item the label of the Z axis <>= char ztitle[P_LEN]; @ \item the color map <>= char colorscale[P_LEN]; @ \item the plot name <>= char name[P_LEN]; @ \item the file option <>= char fileopt[P_LEN]; @ \item heatmap aspect ratio <>= float aspect_ratio; @ \end{itemize} @ \section{Functions} \label{sec:functions} The default properties are loaded with: <>= plotly_properties(void) { strcpy(title,""); strcpy(xtitle,""); strcpy(ytitle,""); strcpy(ztitle,""); strcpy(colorscale,"Portland"); strcpy(name,""); xData = NULL; yData = NULL; zData = NULL; aspect_ratio = 1.0; } @ \subsection{Set plotly properties} \label{sec:set-plotly-prop} <>= int plotly_properties::set(const char *param, const char *value) { int s = -1; s = strcmp("filename",param); if (s==0) { strcpy(filename,value); return s; } s = strcmp("title",param); if (s==0) { strcpy(title,value); return s; } s = strcmp("xtitle",param); if (s==0) { strcpy(xtitle,value); return s; } s = strcmp("ytitle",param); if (s==0) { strcpy(ytitle,value); return s; } s = strcmp("ztitle",param); if (s==0) { strcpy(ztitle,value); return s; } s = strcmp("colorscale",param); if (s==0) { strcpy(colorscale,value); return s; } s = strcmp("name",param); if (s==0) { strcpy(name,value); return s; } return s; } <>= int plotly_properties::set(const char *param, float *value, int N) { int s = -1; s = strcmp("xdata",param); if (s==0) { xData = (char *)malloc(8*N*sizeof(char)*2); s = x2json(xData, value, N); return s; } s = strcmp("ydata",param); if (s==0) { yData = (char *)malloc(8*N*sizeof(char)*2); s = y2json(yData, value, N); return s; } return s; } <>= int plotly_properties::set(const char *param, float *value, int N, int M) { int s = -1, nel; s = strcmp("zdata",param); if (s==0) { free( zData ); nel = N*M; zData = (char *)malloc(8*nel*sizeof(char)*2); s = z2json(zData, value, N, M); return s; } return s; } @ \subsection{JSON} @ The [[buffer]] array is of size $[[size[0]]]\times[[size[1]]]$ written into a json structure: <>= int z2json(char *zData, float *zdata, int N, int M) { int zDataLen, ii, jj; zDataLen = 0; zDataLen += sprintf( zData + zDataLen, "\"z\":[ "); for (ii=0; ii>= int x2json(char *zData, float *zdata, int N) { int zDataLen, ii; zDataLen = 0; zDataLen += sprintf( zData + zDataLen, "\"x\":[ "); for (ii=0; ii>= int y2json(char *zData, float *zdata, int N) { int zDataLen, ii; zDataLen = 0; zDataLen += sprintf( zData + zDataLen, "\"y\":[ "); for (ii=0; ii>= data = (char *)malloc((strlen(p->zData)+2048)*sizeof(char)); int len=0; len += sprintf(data + len,"un=%s&", USERNAME); len += sprintf(data + len,"key=%s&", KEY); len += sprintf(data + len,"origin=%s&", "plot"); len += sprintf(data + len,"platform=%s&","lisp"); len += sprintf(data + len,"args=[{"); if (p->xData!=NULL) len += sprintf(data + len,"%s,",p->xData); if (p->yData!=NULL) len += sprintf(data + len,"%s,",p->yData); len += sprintf(data + len,"%s,",p->zData); len += sprintf( data + len, "\"zauto\": %s," ,"true"); len += sprintf( data + len, "\"colorbar\": {"); len += sprintf( data + len, "\"autotick\": %s," ,"true"); len += sprintf( data + len, "\"title\": \"%s\"",p->ztitle); len += sprintf( data + len, "}"); len += sprintf( data + len, "}]&"); // kwargs > len += sprintf(data + len,"kwargs={"); len += sprintf(data + len, "\"filename\": \"CEO/%s\",",p->filename); len += sprintf(data + len, "\"fileopt\": \"%s\"," ,"overwrite"); // style > len += sprintf(data + len, "\"style\": {"); len += sprintf(data + len, "\"type\": \"%s\",","heatmap"); len += sprintf(data + len, "\"colorscale\": \"%s\"",p->colorscale); len += sprintf(data + len, "},"); // < style // layout > len += sprintf(data + len, "\"layout\": {"); len += sprintf(data + len, "\"title\": \"%s\",",p->title); // xaxis > len += sprintf(data + len, "\"xaxis\": {"); len += sprintf(data + len, "\"title\": \"%s\"",p->xtitle); len += sprintf(data + len, "},"); // < xaxis // yaxis > len += sprintf(data + len, "\"yaxis\": {"); len += sprintf(data + len, "\"title\": \"%s\"",p->ytitle); len += sprintf(data + len, "},"); // < yaxis int width, height; width = 600; height = 580; if (p->aspect_ratio>1) width = (int) ( 200. + 420.*p->aspect_ratio); if (p->aspect_ratio<1) height = (int) ( 160. + 420./p->aspect_ratio ); len += sprintf(data + len, "\"width\": %d," ,width); len += sprintf(data + len, "\"height\": %d," ,height); len += sprintf(data + len, "\"autosize\": %s," ,"false"); // margin > len += sprintf(data + len, "\"margin\": {"); len += sprintf(data + len, "\"l\": %d," ,80); len += sprintf(data + len, "\"r\": %d," ,120); len += sprintf(data + len, "\"t\": %d," ,80); len += sprintf(data + len, "\"b\": %d," ,80); len += sprintf(data + len, "\"pad\": %d," ,0); len += sprintf(data + len, "\"autoexpand\": %s" ,"false"); len += sprintf(data + len, "}"); // < margin len += sprintf(data + len, "},"); // < layout len += sprintf(data + len, "\"world_readable\": %s" ,"true"); len += sprintf(data + len,"}"); <>= data = (char *)malloc((strlen(p->xData)+strlen(p->yData)+2048)*sizeof(char)); int len=0; len += sprintf(data + len,"un=%s&", USERNAME); len += sprintf(data + len,"key=%s&", KEY); len += sprintf(data + len,"origin=%s&", "plot"); len += sprintf(data + len,"platform=%s&","lisp"); len += sprintf(data + len,"args=[{"); len += sprintf(data + len,"%s,",p->xData); len += sprintf(data + len,"%s,",p->yData); len += sprintf( data + len,"\"name\": \"%s\"" ,p->name); len += sprintf( data + len, "}]&"); // kwargs > len += sprintf(data + len,"kwargs={"); len += sprintf(data + len, "\"filename\": \"CEO/%s\",",p->filename); len += sprintf(data + len, "\"fileopt\": \"%s\"," ,p->fileopt); // style > len += sprintf(data + len, "\"style\": {"); len += sprintf(data + len, "\"type\": \"%s\"","scatter"); len += sprintf(data + len, "},"); // < style // layout > len += sprintf(data + len, "\"layout\": {"); len += sprintf(data + len, "\"title\": \"%s\",",p->title); // xaxis > len += sprintf(data + len, "\"xaxis\": {"); len += sprintf(data + len, "\"title\": \"%s\"",p->xtitle); len += sprintf(data + len, "},"); // < xaxis // yaxis > len += sprintf(data + len, "\"yaxis\": {"); len += sprintf(data + len, "\"title\": \"%s\"",p->ytitle); len += sprintf(data + len, "},"); // < yaxis len += sprintf(data + len, "\"width\": %d," ,900); len += sprintf(data + len, "\"height\": %d," ,500); len += sprintf(data + len, "\"autosize\": %s," ,"false"); // margin > len += sprintf(data + len, "\"margin\": {"); len += sprintf(data + len, "\"l\": %d," ,80); len += sprintf(data + len, "\"r\": %d," ,250); len += sprintf(data + len, "\"t\": %d," ,80); len += sprintf(data + len, "\"b\": %d," ,80); len += sprintf(data + len, "\"pad\": %d," ,0); len += sprintf(data + len, "\"autoexpand\": %s" ,"false"); len += sprintf(data + len, "}"); // < margin len += sprintf(data + len, "},"); // < layout len += sprintf(data + len, "\"world_readable\": %s" ,"true"); len += sprintf(data + len,"}"); /* FILE *fp; fp=fopen("plotly.json", "wb"); fprintf(fp,"%s\n",data); fclose(fp); */ <>= free(data); @ The json string is sent to plotly server using libcurl: <>= void curlplotly(void *json) { char dataIn[1024]; char *data; CURLcode ret; CURL *hnd; int len; data = (char *) json; len = strlen(data); hnd = curl_easy_init(); curl_easy_setopt(hnd, CURLOPT_URL, "https://plot.ly/clientresp"); curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(hnd, CURLOPT_WRITEDATA, dataIn); curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE_LARGE, len); curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, data); curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); curl_easy_setopt(hnd, CURLOPT_POST, 1); ret = curl_easy_perform(hnd); if (ret!=0) fprintf(stderr,"CURL FAILED WITH: %s\n",curl_easy_strerror(ret)); jsmn_parser parser; jsmn_init(&parser); jsmntok_t tokens[256]; // jsmnerr_t r; jsmn_parse(&parser, dataIn, strlen(dataIn), tokens, 200); plotly_resp resp; char *resp_ptr[5]; resp_ptr[0] = resp.url; resp_ptr[1] = resp.message; resp_ptr[2] = resp.warning; resp_ptr[3] = resp.filename; resp_ptr[4] = resp.error; int k, idx; for (k=0;k<5;k++) { idx = 2*(k+1); sprintf(resp_ptr[k],"%.*s", (tokens[idx]).end-(tokens[idx]).start, dataIn + (tokens[idx]).start); } if (strlen(resp.error)==0) { /* char command[50]; */ /* sprintf( command, "firefox %s&", resp.url); */ /* system(command); */ fprintf(stdout,"\x1B[36m >>> %s\x1B[0m\n",resp.url); if (strlen(resp.message)!=0) fprintf(stdout,"@(CEO)>plotly: message: %s\n",resp.message); } else { fprintf(stderr,"@(CEO)>plotly: error: %s\n",resp.error); FILE *fp; fp=fopen("plotly.json", "wb"); fprintf(fp,"%s\n",data); fclose(fp); } curl_easy_cleanup(hnd); hnd = NULL; } @ The response of the plotly server is capture with: <>= size_t write_data(void *ptr, size_t size, size_t count, void *stream) { memcpy(stream, ptr, size * count+1); return count*size; } @ \subsection{imagesc} \label{sec:imagesc} <>= void imagesc(plotly_properties *p) { fprintf(stdout,"\x1B[36m@(CEO)>plotly: %s\x1B[0m",p->filename); <> } @ For each graph a thread is created: <>= char *data; <> curlplotly( (void *) data); <> @ \subsection{plot} \label{sec:plot} <>= void plot(plotly_properties *p) { fprintf(stdout,"\x1B[36m@(CEO)>plotly: %s\x1B[0m",p->filename); char *data; <> curlplotly( (void *) data); <> } @ \section{Test} \label{sec:test} <>= #ifndef __CEO_H__ #include "ceo.h" #endif void graph( int N, char * filename) { int ii, nel; float *z; printf("=> matrix size:%d^2\n",N); nel = N; nel *= nel; z = (float *)malloc(nel*sizeof(float)); srand(time(NULL)); for (ii=0;ii