Wiki Discussion: Generating Cytoscape.js graphs with SWI-Prolog

A most simple single page JSON data exchange example, to be further expanded for interactivity.

/*  File:    cyajax.pl
    Author:  Carlo
    Created: Sep 13 2020
    Purpose: feeding CytoscapeJS with AJAX/JSON
*/

:- module(cyajax, []).

:- use_module(library(http/http_server)).
:- use_module(library(http/http_json)).
:- use_module(library(http/http_log)).
:- use_module(library(ugraphs)).

% when `make` just reload http://localhost:8081 to get changes
:- initialization (
       Port=8081,
       catch(http_stop_server(Port,[]),_,true),
       http_server([port(Port)])
   ).

:- http_handler(root(.),
                http_redirect(moved,location_by_id(home_page)),
                []).
:- http_handler(root(home),home_page,[]).

:- http_handler(root(run_cyajax),run_cyajax,[]).

home_page(_Request) :-
    reply_html_page(
        [ title('cyajax test'),
          \cy_cdn
        ],
        [ div([
            input([id=run_cyajax,type=button,value='run cyajax']),
            div([id=host_cy],'host cy')
          ]),
          \css,
          \js
        ]).

example_graph(1,
  [1-[2,3],2-[3,4,5],3-[1,2],4-[1,3],5-[4,5]]).

ugraph_to_cy_elements(G,Elements) :-
    vertices(G,Vs),
    maplist([V,_{data:_{id:V}}]>>true,Vs,Nodes),
    edges(G,Es),
    maplist([Source-Target,
             _{data:_{id:EdgeId,source:Source,target:Target}}
            ]>>format(string(EdgeId),"~w->~w",[Source,Target])
            ,Es,Edges),
    append(Nodes,Edges,Elements).

run_cyajax(Request) :-
  http_read_json_dict(Request,Dict),
  example_graph(Dict.example,G),
  ugraph_to_cy_elements(G,Es),
  reply_json_dict(_{elements:Es,layout:cose}).

cy_cdn -->
    html(script([src=
      'https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.15.2/cytoscape.min.js'
    ],'')).

css --> html(style('

#host_cy {
  height: 400px;
  width: 400px;
  background-color: lime;
}

')).

js --> html(script("

window.onload = () => {

  const request = (url,json) => fetch(url,{
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(json)
  }).then(r => r.json())

  run_cyajax.onclick = async (ev) => {
    try {
      render_cy(await request('/run_cyajax', {example:1}))
    } catch(e) {
      alert(e.message)
    }
  }

  render_cy = (from_pl) => {
    var cy = {
        container: host_cy,
        style: [
            {   selector: 'node',
                style: {
                    'background-color': '#666',
                    'label': 'data(id)'
                }
            },
            {   selector: 'edge',
                style: {
                    'target-arrow-shape': 'triangle',
                    'width': 3,
                    'line-color': '#ddd',
                    'target-arrow-color': '#ddd',
                    'curve-style': 'bezier'
                }
            }
        ],
    }
    if (from_pl.elements)
      cy.elements = from_pl.elements
    cytoscape(cy).makeLayout({ name: from_pl.layout }).run()
  }
}

")).
2 Likes