cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Unable to insert zoom in and zoom out buttons inside neovis.js

sucheta
Graph Buddy

Hi,

As i believe Neovis.js is made from vis.js graph network, i tried to implement built in interactiveButtons option in the vis options . But it is not wokring. Can you please help in implementing a physical zoom-in and zoom-out button in Neovis.js. The code snippet is -

         onCompleted: function () {
                session.close();
               
        
              let options = {

                 interaction: {
                    navigationButtons: true,
                    keyboard: true
                },
                
                nodes: {
                    shape: 'dot',
                    font: {
                        size: 26,
                        strokeWidth: 7
                    },
                    scaling: {
                        label: {
                            enabled: true
                        }
                    }
                },
                edges: {
                    arrows: {
                        to: {enabled: self._config.arrows || false } // FIXME: handle default value
                    },
                    length: 200
                },
                layout: {
                    improvedLayout: false,
                    hierarchical: {
                        enabled: self._config.hierarchical || false,
                        sortMethod: self._config.hierarchical_sort_method || "hubsize"

                    }
                },

              };

          //code
           }

vis.js reference - http://visjs.org/docs/network/interaction.html#

Awaiting a reply.

6 REPLIES 6

sucheta
Graph Buddy

Thank you. I successfully implemented a manual zoom-in, zoom-out button in the Neovis.js screen by inserting interaction option -

   onCompleted: function () {
                session.close();
               
                          
              let options = {

                nodes: {
                    shape: 'dot',
                    font: {
                        size: 26,
                        strokeWidth: 7
                    },
                    scaling: {
                        label: {
                            enabled: true
                        }
                    }
                },

                interaction: {
                    hover: true,
                    keyboard: {
                        enabled: true,
                        bindToWindow: false
                    },
                    navigationButtons: true,
                    tooltipDelay: 1000000,
                    hideEdgesOnDrag: true,
                    zoomView: false
                },

                edges: {
                    arrows: {
                        to: {enabled: self._config.arrows || false } // FIXME: handle default value
                    },
                    length: 200
                },
                layout: {
                    improvedLayout: false,
                    hierarchical: {
                        enabled: self._config.hierarchical || false,
                        sortMethod: self._config.hierarchical_sort_method || "hubsize"

                    }
                },

              };

            var container = self._container;
            self._data = {
                "nodes": new vis.DataSet(Object.values(self._nodes)),
                "edges": new vis.DataSet(Object.values(self._edges))

            }

            console.log(self._data.nodes);
            console.log(self._data.edges);
            
          
            self._network = new vis.Network(container, self._data, options);
            console.log("completed");
            setTimeout(() => { self._network.stopSimulation(); }, 10000);

            self._events.generateEvent(CompletionEvent, {record_count: recordCount});

            },
            onError: function (error) {
              console.log(error);
            }

        })
    };

Reference link - https://github.com/almende/vis/issues/3021

Perhaps you can send a pull request to neovis

Okay. I have implemented a spinner also inside neovis, so that till the data loads , we get a spinner asking the user to wait.

sucheta
Graph Buddy

Hi Michael,

I am unable to push it into the neovis.js git account. I am unable to do that because -

I guess the master is empty that's why.
Please suggest.

You need to make a fork and do the edits in your fork and then send a pull request.

HI micheal,

My neovis.js code with updated zoom-in and zoom out and spinner features are given below -

'use strict';

import * as neo4j from '../vendor/neo4j-javascript-driver/lib/browser/neo4j-web.js';
import * as vis from '../vendor/vis/dist/vis-network.min.js';
import '../vendor/vis/dist/vis-network.min.css';
import { defaults } from './defaults';
import { EventController, CompletionEvent } from './events';


//self
//import {Spinner} from 'spin.js';

export default class NeoVis {

    /**
     *
     * @constructor
     * @param {object} config - configures the visualization and Neo4j server connection
     *  {
     *    container:
     *    server_url:
     *    server_password?:
     *    server_username?:
     *    labels:
     *
     *  }
     *
     */


    constructor(config) {
        console.log(config);
        console.log(defaults);
        // .encrypted
        this._config = config;
        this._encrypted = config.unencrypted || defaults['neo4j']['unencrypted'];
        this._trust = config.trust || defaults.neo4j.trust;
        this._driver = neo4j.v1.driver(config.server_url || defaults.neo4j.neo4jUri, neo4j.v1.auth.basic(config.server_user || defaults.neo4j.neo4jUser, config.server_password || defaults.neo4j.neo4jPassword), {encrypted: this._encrypted, trust: this._trust});
        this._query =   config.initial_cypher || defaults.neo4j.initialQuery;
        this._nodes = {};
        this._edges = {};
        this._data = {};
        this._network = null;
        this._container = document.getElementById(config.container_id);
        this._events = new EventController();
    }

    _addNode(node) {
        this._nodes[node.id] = node;
    }

    _addEdge(edge) {
        this._edges[edge.id] = edge;
    }

    /**
     * Build node object for vis from a neo4j Node
     * FIXME: use config
     * FIXME: move to private api
     * @param n
     * @returns {{}}
     */
     buildNodeVisObject(n) {

        var self = this;
        let node = {};
        let label = n.labels[0];

        let captionKey   = this._config && this._config.labels && this._config.labels[label] && this._config.labels[label]['caption'],
            sizeKey = this._config && this._config.labels && this._config.labels[label] && this._config.labels[label]['size'],
            sizeCypher = this._config && this._config.labels && this._config.labels[label] && this._config.labels[label]['sizeCypher'],
            communityKey = this._config && this._config.labels && this._config.labels[label] && this._config.labels[label]['community'];

        node['id'] = n.identity.toInt();
        
        // node size

        if (sizeCypher) {
            // use a cypher statement to determine the size of the node
            // the cypher statement will be passed a parameter {id} with the value
            // of the internal node id

            let session = this._driver.session();
            session.run(sizeCypher, {id: neo4j.v1.int(node['id'])})
                .then(function(result) {
                    result.records.forEach(function(record) {
                        record.forEach(function(v,k,r) {
                            if (typeof v === "number") {
                                self._addNode({id: node['id'], value: v});
                            } else if (v.constructor.name === "Integer") {
                                self._addNode({id: node['id'], value: v.toNumber()})
                            }
                        })
                    })
                })


        } else if (typeof sizeKey === "number") {
            node['value'] = sizeKey;
        } else {

            let sizeProp = n.properties[sizeKey];

            if (sizeProp && typeof sizeProp === "number") {
                // propety value is a number, OK to use
                node['value'] = sizeProp;
            } else if (sizeProp && typeof sizeProp === "object" && sizeProp.constructor.name === "Integer") {
                // property value might be a Neo4j Integer, check if we can call toNumber on it:
                if (sizeProp.inSafeRange()) {
                    node['value'] = sizeProp.toNumber();
                } else {
                    // couldn't convert to Number, use default
                    node['value'] = 1.0;
                }
            } else {
                node['value'] = 1.0;
            }
        }

        // node caption
        if (typeof captionKey === "function") {
            node['label'] = captionKey(n);
        }
        else {
            node['label'] = n.properties[captionKey] || label || "";
        }

        // community
        // behavior: color by value of community property (if set in config), then color by label
        if (!communityKey) {
            node['group'] = label;
        } else {
            try {
                if (n.properties[communityKey]) {
                    node['group'] = n.properties[communityKey].toNumber() || label || 0;  // FIXME: cast to Integer

                }
                else {
                    node['group'] = 0;
                }

            } catch(e) {
                node['group'] = 0;
            }

            
        }


        // set all properties as tooltip
        node['title'] = "";
        for (let key in n.properties) {
            node['title'] += "<strong>" + key + ":</strong>" + " " + n.properties[key] + "<br>";
        }
        return node;
    }

    /**
     * Build edge object for vis from a neo4j Relationship
     * @param r
     * @returns {{}}
     */
    buildEdgeVisObject(r) {

        let weightKey = this._config && this._config.relationships && this._config.relationships[r.type] && this._config.relationships[r.type]['thickness'],
            captionKey = this._config && this._config.relationships && this._config.relationships[r.type] && this._config.relationships[r.type]['caption'];

        let edge = {};
        edge['id'] = r.identity.toInt();
        edge['from'] = r.start.toInt();
        edge['to'] = r.end.toInt();

        // hover tooltip. show all properties in the format <strong>key:</strong> value
        edge['title'] = "";
        for (let key in r.properties) {
            edge['title'] += "<strong>" + key + ":</strong>" + " " + r.properties[key] + "<br>";
        }

        // set relationship thickness
        if (weightKey && typeof weightKey === "string") {
            edge['value'] = r.properties[weightKey];
        } else if (weightKey && typeof weightKey === "number") {
            edge['value'] = weightKey;
        } else {
            edge['value'] = 1.0;
        }

        // set caption


        if (typeof captionKey === "boolean") {
            if (!captionKey) {
                edge['label'] = "";
            } else {
                edge['label'] = r.type;
            }
        } else if (captionKey && typeof captionKey === "string") {
            edge['label']  = r.properties[captionKey] || "";
        } else {
            edge['label'] = r.type;
        }

        return edge;
    }

    // public API

    render() {

        // connect to Neo4j instance
        // run query


    var opts = {
        lines: 15, // The number of lines to draw
        length: 20, // The length of each line
        width: 12, // The line thickness
        radius: 16, // The radius of the inner circle
        scale: 1, // Scales overall size of the spinner
        corners: 1, // Corner roundness (0..1)
        color: '#456789', // CSS color or array of colors
        fadeColor: 'transparent', // CSS color or array of colors
        speed: 1, // Rounds per second
        rotate: 0, // The rotation offset
        animation: 'spinner-line-fade-more', // The CSS animation name for the lines
        direction: 1, // 1: clockwise, -1: counterclockwise
        zIndex: 2e9, // The z-index (defaults to 2000000000)
        className: 'spinner', // The CSS class to assign to the spinner
        top: '500%', // Top position relative to parent
        left: '720%', // Left position relative to parent
        shadow: '0 0 1px transparent', // Box-shadow for the lines
        position: 'absolute' // Element positioning
      };
      

      //Spinner viz

      var target = document.getElementById('viz');
      var spinner = new Spinner(opts).spin(target);

       //Spinner viz ends



               

                // //-----------  Machine-merger spinner ends  -----------

                // //============   Duplicity spinner   ================


                var targetCollection = document.getElementById('vizCollection');
                var spinner =new Spinner(opts).spin(targetCollection);


               



                //  Spinner ends.


         //    var spinner = new Spinner().spin();
         //    target.appendChild(spinner.el);


        let self = this;
        let recordCount = 0;

        let session = this._driver.session();
        session
            .run(this._query, {limit: 30})
            .subscribe({
                onNext: function (record) {
                    recordCount++;

                    console.log("CLASS NAME");
                    console.log(record.constructor.name);
                    console.log(record);

                    record.forEach(function(v, k, r) {
                    console.log("Constructor:");
                    console.log(v.constructor.name);
                    if (v.constructor.name === "Node") {
                        let node = self.buildNodeVisObject(v);

                        try {
                            self._addNode(node);
                        } catch(e) {
                            console.log(e);
                        }

                    }
                    else if (v.constructor.name === "Relationship") {

                        let edge = self.buildEdgeVisObject(v);

                        try {
                            self._addEdge(edge);
                        } catch(e) {
                            console.log(e);
                        }

                    }
                    else if (v.constructor.name === "Path") {
                        console.log("PATH");
                        console.log(v);
                        let n1 = self.buildNodeVisObject(v.start);
                        let n2 = self.buildNodeVisObject(v.end);
                        
                        self._addNode(n1);
                        self._addNode(n2);

                        v.segments.forEach((obj) => {
                            
                            self._addNode(self.buildNodeVisObject(obj.start));
                            self._addNode(self.buildNodeVisObject(obj.end))
                            self._addEdge(self.buildEdgeVisObject(obj.relationship))
                        });

                    }
                    else if (v.constructor.name === "Array") {
                        v.forEach(function(obj) {
                            console.log("Array element constructor:");
                            console.log(obj.constructor.name);
                            if (obj.constructor.name === "Node") {
                                let node = self.buildNodeVisObject(obj);

                                try {
                                    self._addNode(node);
                                } catch(e) {
                                    console.log(e);
                                }
                            }
                            else if (obj.constructor.name === "Relationship") {
                                let edge = self.buildEdgeVisObject(obj);

                                try {
                                    self._addEdge(edge);
                                } catch(e) {
                                    console.log(e);
                                }
                            }
                        });
                    }

                })
                },
                onCompleted: function () {
                    session.close();
                   
            
                //target.appendChild(spinner.el);


                  
                  let options = {

                  //  autoResize: true,
                   // height: '600',
                  //  width: '100%',
                  
                    nodes: {
                        shape: 'dot',
                        font: {
                            size: 26,
                            strokeWidth: 7
                        },
                        scaling: {
                            label: {
                                enabled: true
                            }
                        }
                    },

                    interaction: {
                        hover: true,
                        keyboard: {
                            enabled: true,
                            // bindToWindow: false
                            bindToWindow: false
                        },
                        navigationButtons: true,
                        tooltipDelay: 1000000,
                        hideEdgesOnDrag: true,
                        // zoomView: false
                        zoomView: true
                    },

                    edges: {
                        arrows: {
                            to: { enabled: self._config.arrows || false } // FIXME: handle default value
                        },
                        length: 200
                    },
                    layout: {
                        improvedLayout: false,
                        hierarchical: {
                            enabled: self._config.hierarchical || false,
                            sortMethod: self._config.hierarchical_sort_method || "hubsize"

                        }
                    },



                 
                  };

                var container = self._container;
                self._data = {
                    "nodes": new vis.DataSet(Object.values(self._nodes)),
                    "edges": new vis.DataSet(Object.values(self._edges))

                }

                console.log(self._data.nodes);
                console.log(self._data.edges);
                
               
                
                self._network = new vis.Network(container, self._data, options);
                console.log("completed");
                setTimeout(() => { self._network.stopSimulation(); }, 10000);

                //spinner
                var target = document.getElementById('viz');
                var spinner =new Spinner(opts).stop(target);


                //Machine-Merger spinner


                //-----------  Machine-merger spinner ends  -----------

                //============   Duplicity spinner   ================


                var targetCollection = document.getElementById('vizCollection');
                var spinner =new Spinner(opts).stop(targetCollection);


               

                
               



                //================  Duplicity spinner ends  ===================


                // --------------------  spinner ends  -----------------------

                self._events.generateEvent(CompletionEvent, {record_count: recordCount});

                },
                onError: function (error) {
                  console.log(error);
                }

            })
        };

    /**
     * Clear the data for the visualization
     */
    clearNetwork() {
        this._nodes = {}
        this._edges = {};
        this._network.setData([]);
    }

// Zoom In and Zoom Out functions.

 zoomin() {
    var myImg = document.getElementById("viz");
    var currWidth = myImg.clientWidth;
    if (currWidth == 2500) return false;
    else {
      myImg.style.width = (currWidth + 100) + "px";
    }
  }
  
  zoomout() {
    var myImg = document.getElementById("viz");
    var currWidth = myImg.clientWidth;
    if (currWidth == 100) return false;
    else {
      myImg.style.width = (currWidth - 100) + "px";
    }
  }





    /**
     *
     * @param {string} eventType Event type to be handled
     * @param {callback} handler Handler to manage the event
     */
    registerOnEvent(eventType, handler) {
        this._events.register(eventType, handler);
    }


    /**
     * Reset the config object and reload data
     * @param config
     */
    reinit(config) {

    };

    /**
     * Fetch live data form the server and reload the visualization
     */
    reload() {

        this.clearNetwork();
        this.render();


    };

    /**
     * Stabilize the visuzliation
     */
    stabilize() {
        this._network.stopSimulation();
        console.log("Calling stopSimulation");
    }

    /**
     * Execute an arbitrary Cypher query and re-render the visualization
     * @param query
     */
    renderWithCypher(query) {

        //self._config.initial_cypher = query;

        this.clearNetwork();
        this._query = query;
        this.render();

    };

    

}

in index.html

<!-- spin.js library -->
 <script src="http://cdnjs.cloudflare.com/ajax/libs/spin.js/1.2.7/spin.min.js" />