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

For those wanting to get a taste of creating Cytoscape.js graphs using SWI-Prolog read Convert facts to GraphViz dot file using DCGs then find and work the tutorials in the Cytoscape.js blog.

The tutorials seem to be dated and gloss over some needed points so they are not a straight forward following the tutorial, you will have to draw upon your knowledge of HTML, JavaScript, CSS, JSON and SVG to get them to work.

I have completed the first tutorial: Getting started with Cytoscape.js, and currently working through Visualizing Glycolysis with Cytoscape.js.


In searching for a way to use graphs more interactively ran across animations with D3. D3 has animations, and it appears Cytoscape.js might have something similar, but have not found a similar working example just yet.

Check out this D3 animation and then think about teaching Prolog to new users needing a visual animated example as an answer and you start to see how much these become useful.

Here is an example of a Cytoscape.js animation: Animated BFS


Related:

swish_cytoscape_js
https://swish.swi-prolog.org/example/render_c3.swinb


Personal notes

The Cytoscape.js tutorial demo gives a very simple demonstration of what can be done with Cytoscape.js. The code in in a GitHub repository that is designed for adding custom elements systems and Stylesheets and/or layouts.


Note: Using background-image one has to be sensitive to: CORS header ‘Access-Control-Allow-Origin’ missing.
Cross-Origin Resource Sharing (CORS)


node.js - Useful for running JavaScript as a server.

node.js http-server is a simple, zero-configuration command-line http server. It is powerful enough for production usage, but it’s simple and hackable enough to be used for testing, local development, and learning.

Steps to test Cytoscape.js tutorials on a local machine. Think workaround to avoid CORS errors.

  1. Download and install node.js
    Download | Node.js
  2. Install http-server package
    npm install --global http-server
  3. Change to directory that is the root of the files for the web site
  4. Create batch file to set Windows Path and start http-server
SET PATH="C:\Program Files (x86)\nodejs";"C:\Users\<user>\AppData\Roaming\npm";"C:\Users\<user>\AppData\Roaming\npm\node_modules\http-server"
http-server --cors

Start http-server by running batch file
http_server.bat
Using Internet browser access web pages
http://127.0.0.1:8080

This is also useful for access individual image files to make sure they load and display, e.g.

http://127.0.0.1:8080/glucose.svg

[Mon Aug 24 2020 15:03:50 GMT-0400 (Eastern Daylight Time)]  "GET /glucose.svg" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"

When running the Node.js http-server it will list actions and errors in the Windows console.

[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/13bpg.svg" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/glucose.svg" Error (404): "Not found"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/g6p.svg" Error (404): "Not found"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/f6p.svg" Error (404): "Not found"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/f16bp.svg" Error (404): "Not found"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/2pg.svg" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/pep.svg" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/gadp.svg" Error (404): "Not found"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/13bpg.svg" Error (404): "Not found"
[Tue Aug 25 2020 07:46:34 GMT-0400 (Eastern Daylight Time)]  "GET /assets/pyruvate.svg" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041"

Remember to press F12 key when running the Internet browser to see any errors. This is where the CORS errors are reported.


Chrome Internet browser

Chrome Dev Tools

How to Completely Disable Cache in Google Chrome

Google Chrome - Clear cache & cookies

Open Chrome debugger in separate window (ref)

View Page Resources With Chrome DevTools
such as HTML, CSS, JavaScript, JSON. (ref)


SVG node background tutorial
http://embed.plnkr.co/wLuoon/

To see image height and width of an image in Chrome: Image Size Info - Browser Extension

JavaScript Errors - Throw and Try to Catch

Create SVG file by drawing.
Online tool: Method Draw
Application: Inkscape

Scalable Vector Graphics (SVG) 1.1 (Second Edition)

Should you use or learn jQuery in 2020?

Alignment constraints with guidelines

cytoscape - Libraries - cdnjs - The #1 free and open source CDN built to make life easier for developers

Web Open Font Format

JSONLint - The JSON Validator

Why are there warnings about DevTools failed to load source map?
See: Use a source map

Cytoscape.js can import/export the elements as JSON (cy.json()) and supports two different formats (ref)

  1. array of collections
  2. array of elements

NB The nodes for a graph are AFAIK not accessible from from an Internet Browser console. They are obviously accessible in from JavaScript of the source document.

To export the cytoscape graph as JSON

  1. Create a button in the HTML body tag
<input type='button' id='advance' value='Export'>
  1. Add a listener event for the button. This goes within document.addEventListener("DOMContentLoaded", function ()
document.addEventListener("DOMContentLoaded", function () {
var toJSONButton = document.getElementById('advance');
    toJSONButton.addEventListener('click', function () {
        var result = toJsonButton();
    });
}
  1. Add handler for listener. This goes within document.addEventListener("DOMContentLoaded", function ()
document.addEventListener("DOMContentLoaded", function () {
    function toJsonButton() {
        var cyFlatJson = cy.json(true);
        var cyGroupJson = cy.json(false);
        console.log(JSON.stringify(cyFlatJson));
        console.log(JSON.stringify(cyGroupJson));
    }
});

Clicking on the Export button will write two strings to the browser console. The browser console is accessible by clicking F12 in an open Internet browser.


GitHub repositories
Topics: Cytoscape.js

cytoscape/cytoscape.js
plotly/dash-cytoscape - Has some interesting ideas and examples. Uses Python with Cytoscape.js

i-Vis at Bilkent - i-Vis Research Lab at Bilkent University
Has many repositories related to Cytoscape.js - The code is worth studying for examples.

Newt Pathway Viewer & Editor - Newt is a free, web based, open source viewer and editor for pathways in Systems Biological Graphical Notation (SBGN) and Simple Interaction Format (SIF). It was written with a series of libraries and extensions based on Cytoscape.js with utmost customization in mind. (Think Visio).
http://web.newteditor.org/ - Press F12 to see code.

demonray/cyeditor - A visual flow chart editor based on cytoscape.js. (Think Visio).

upsetjs/cytoscape.js-bubblesets

lukasy09/NeuralNetworkTool


Using Fetch

A basic fetch request is really simple to set up. Have a look at the following code:

fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));

JavaScript read JSON from URL


HTML

Best Practices In Modern Web Design: The Ultimate Round-Up
HTML <script> defer Attribute

Meta

HTTP headers

Viewport
Useful HTML Meta Tags


JavaScript

The Modern JavaScript Tutorial

Functions (MDN) (w3Schools) (javascript.info)

Arrow function expressions (MDN) (w3schools) (javascript.info)
Promise (MDN) (javascript.info) (pouchdb) (Promises/A+)
Promises chaining (MDN) (javascript.info) (medium.com)

image

Image (ref)
The future of JavaScript isn’t what it used to be


Papers:

A Technique for Drawing Directed Graphs (ref)
Visualisation of state machines using the Sugiyama
framework (ref)


JSON

Sites for testing JSON
Using JSON with SWI-Prolog

TypeScript

Type definitions for Cytoscape.js 3.1 (ref)

Online editor

CodeMirror - used by codepen.io

__

StackOverflow Cytoscape.js Q&A

What is the difference between D3.js and Cytoscape.js?
CytoscapeJS background color grey
I can’t get cytoscape.js to display my chart at all
Conditional styling for cytoscape
How to make cytoscape-ctxmenu.js applicable on nodes with condition?
Different behaviour for leaf nodes cytoscape
Show edge id on click
Find selected nodes/edges in cytoscape.js “on(‘unselect’)” event
Show dependet edge(s) when user click on node
How to draw an edge between two node in cytoscape with mouse drag drop
How do I reference ID as a variable in cytoscape.js
Cytoscape: show node/edge attributes in table when clicking on it
Set link behind background image and make it clickable Cytoscape.js
Add diffrenet href to some nodes in cytoscape.js
Rendering a family tree with d3 or cytoscape
How to listen to drag event of CytoscapeJS node (including only the one directly under the cursor/finger)
How to totally disable elements events?
Is there any example or sample code for the find and filter feature in Cytoscape JS
Is is possible to have a bidirectional edge in Cytoscape.js?
What javascript include files are needed to get layout extensions to work in cytoscape?
Use Algrebraic Operation in Cytoscape Selector Style Sheet
Cytoscape.js : right click event on nodes
see the list of event listeners currently attached
Filtering graph by connectivity to a specific node in cytoscape.js
Changing color when node is selected in Cytoscape JS
Cytoscape js how to get all edges (text label) when clicked on a node
CytoscapeJS background color grey
How to highlight neighbouring nodes in Cytoscape.js
How to highlight the path between two nodes in CYTOSCAPE JS
Content and Label on a Node - Cytoscape


When using SWI-Prolog dicts to represent JSON one may run into a few inconsistencies with converting back and forth between JSON and SWI-Prolog dicts. So to help me understand how to do it consistently I created a round robin predicate with examples.

Round robin predciate
:- module(json_to_prolog_dict,
    [
        example/1
    ]).

:- use_module(library(http/json)).

% operator: {}
% Basic structure AKA object.
%
% ?- example(1).
% {}
% "{}"
% true.
example(1) :-
    Dict = {},
    print_term(Dict,[]),
    nl,
    json_write_dict(current_output,Dict).

% operator: :
% Defines name-value paris.
% Note: {"key":"value"} is valid JSON
%       However {key:value} is not a valid SWI-Prolog dict. The dict requires a tag.
%
% ?- example(2).
% object{key:value}
% {"key":"value"}
% true.
example(2) :-
    Dict = object{ key:value },
    print_term(Dict,[]),
    nl,
    json_write_dict(current_output,Dict).

% operator: ,
% Combines same types.
%
% ?- example(3).
% object{key1:value1,key2:value2}
% {"key1":"value1", "key2":"value2"}
% true.
example(3) :-
    Dict = object{ key1:value1, key2:value2 },
    print_term(Dict,[]),
    nl,
    json_write_dict(current_output,Dict).

% []
% Array
% Note: Not allowed as a key
%
% ?- example(4).
% object{key:[]}
% {"key": []}
% true.
example(4) :-
    Dict = object{ key: [] },
    print_term(Dict,[]),
    nl,
    json_write_dict(current_output,Dict).

% Collection of objects using array.
%
% ?- example(5).
% collection{ objects:[ object{data:[]},
%                       object{data:[1]},
%                       object{data:[2,a]}
%                     ]
%           }
% {"objects": [ {"data": []},  {"data": [1 ]},  {"data": [2, "a" ]} ]}
% true.
example(5) :-
    Dict = collection{ objects: [object{ data: [] },object{ data: [1] },object{ data: [2,a] }] },
    print_term(Dict,[]),
    nl,
    json_write_dict(current_output,Dict).

% Note:
%   {} is not a dict however atom_json_dict/2 will correctly convert it to Json.
%  _{} is a dict.
example(20) :-
    Dict = _{},
    round_robin(_Json,Dict).

example(21) :-
    Json = {},
    round_robin(Json,_Dict).

example(22) :-
    Dict = _{ key:value },
    round_robin(_Json,Dict).

example(23) :-
    Json = { "key":"value" },
    round_robin(Json,_Dict).

example(24) :-
    Dict = _{ key1:value1, key2:value2 },
    round_robin(_Json,Dict).

example(25) :-
    Json = { "key1":"value1", "key2":"value2" },
    round_robin(Json,_Dict).

example(26) :-
    Dict = _{ key: [] },
    round_robin(_Json,Dict).

example(27) :-
    Json = { "key": [] },
    round_robin(Json,_Dict).

example(28) :-
    Dict = _{ objects: [_{ data: [] },_{ data: [1] },_{ data: [2,a] }] },
    round_robin(_Json,Dict).

example(29) :-
    Json = { "objects": [ { "data": [] }, { "data": ["1"] }, { "data": ["2","a"] }] },
    round_robin(Json,_Dict).

round_robin(Json0,Dict0) :-
    (
        var(Json0)
    ->
        % atom_json_dict/3 will accept some values that are not valid dicts, e.g. {}
        % Since the goal is to round robin the values, only valid dicts are allowed.
        is_dict(Dict0),
        atom_json_dict(Json0,Dict0,[]),
        atom_json_dict(Json0,Dict1,[value_string_as(atom)]),
        atom_json_dict(Json1,Dict1,[])
    ;
        % Since atom_json_dict/3 will not accept compounds, e.g. { "key":"value" }
        % need to convert input term into an atom.
        term_to_atom(Json0,Json_atom0),
        atom_json_dict(Json_atom0,Dict0,[]),
        atom_json_dict(Json_atom1,Dict0,[]),
        atom_json_dict(Json_atom1,Dict1,[]),
        % Since atom_json_dict/3 outputs atoms and some inputs are not atoms but terms,
        % need to covert atom into a term.
        % Since atoms and strings can be used interchangeably for most SWI-Prolog built-in predicate arguments
        % one can think of term_string/2 as term_atom/2.
        term_string(Json1,Json_atom1)
    ),
    % =@= used to allow for difference in system generated tags, e.g. _65108{} and _36929{} are the same.
    assertion( Dict0 =@= Dict1 ),
    assertion( Json0 == Json1 ).

Python implementation of visualization technique for (sklearn) decision trees. (ref)

2 Likes