Context
I want to use cytoscape.js for visualizing graphs. While I am capable with a myriad of languages (C++, Mathematica, R, etc), I am new to Javascript, JSON, HTML, and CSS. Thus it would be favorable to learn these languages through this use case (implementing graphs with cytoscape.js). Please keep this in mind in your answer.
I have previously asked how to [remotely load cytoscape.js and how to get graphs display (requires a div). Since then I have created a script that turns a graph as represented in one of the other languages I use, into the JSON format indicated here. While I can just copy-paste all of this directly into my program, for large networks that is clearly a poor way to implement it. An example of my script's output is at the bottom of this.
Question
Given a JSON object/file(?) how can I do the following:
load it into cytoscape.js without copy-pasting the code.
referencing it once loaded. (e.g. basic explanation of how JSON syntax for use in cytoscape.js)
Script Output
cytoscape({
container: document.getElementById('cy'),
elements: [
{// node Node 1
group: 'nodes',
data: {
id: 'Node 1'
},
selected: false,
selectable: true,
locked: false,
grabbable: true,
selectable: true,
},
{// node Node 2
group: 'nodes',
data: {
id: 'Node 2'
},
selected: false,
selectable: true,
locked: false,
grabbable: true,
selectable: true,
},
{// node Node 3
group: 'nodes',
data: {
id: 'Node 3'
},
selected: false,
selectable: true,
locked: false,
grabbable: true,
selectable: true,
},
{// edge 1_2
group: 'edges',
data: {
id: '1_2',
source: '1',
target: '2'
}
},
{// edge 2_3
group: 'edges',
data: {
id: '2_3',
source: '2',
target: '3'
}
},
{// edge 3_1
group: 'edges',
data: {
id: '3_1',
source: '3',
target: '1'
}
}
],
style: [
{
selector: 'node',
style: {
'content': 'data(id)'
}
}
]
});
<head>
<title></title>
<script src="js/vendor/cytoscape.min.js"></script>
<script src="js/vendor/jquery.min.js"></script>
</head>
<style>
#cy {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
}
</style>
<body>
<div id="cy"></div>
<script>
$.getJSON("cyto.js", function (data) {
//console.log(data);
var cy = cytoscape({
container: document.getElementById('cy'),
elements: data,
style: [
{
selector: 'node',
style: {
'label': 'data(label)',
'width': '60px',
'height': '60px',
'color': 'blue',
'background-fit': 'contain',
'background-clip': 'none'
}
}, {
selector: 'edge',
style: {
'text-background-color': 'yellow',
'text-background-opacity': 0.4,
'width': '6px',
'target-arrow-shape': 'triangle',
'control-point-step-size': '140px'
}
}
],
layout: {
name: 'circle'
}
});
});
</script>
</body>
in cyto.js you can input valid JSON data, for example
{
"nodes": [
{
"data": {"id": "a", "label": "Gene1"}
},
{
"data": {"id": "b", "label": "Gene2"}
},
{
"data": {"id": "c", "label": "Gene3"}
},
{
"data": {"id": "d", "label": "Gene4"}
},
{
"data": {"id": "e", "label": "Gene5"}
},
{
"data": {"id": "f", "label": "Gene6"}
}
],
"edges": [
{
"data": {
"id": "ab",
"source": "a",
"target": "b"
}
},
{
"data": {
"id": "cd",
"source": "c",
"target": "d"
}
},
{
"data": {
"id": "ef",
"source": "e",
"target": "f"
}
},
{
"data": {
"id": "ac",
"source": "a",
"target": "d"
}
},
{
"data": {
"id": "be",
"source": "b",
"target": "e"
}
}]
}
Let's presume you have a json file in the same folder as your 'index.html', and your server is running. First request the json file via a http request (using plain javascript or jquery ).
If your json file has the same format as the elements properties, you can just parse it to a javascript object and set it. E.g.
var myObject = {};
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myObject = JSON.parse(this.responseText);
initCytoscape();
}
};
xhttp.open("GET", "myJson.json", true);
xhttp.send();
function initCytoscape() {
cytoscape({
container: document.getElementById('cy'),
elements: myObject
});
}
if the json property is different than cytoscape's format, then you have to programatically convert it.
Related
I'm trying to use server grouping in a grid.
I'm not sure about the difference between "schema.groups" and "schema.data". I understand that i should use shema.data when data are not grouped, and schema.groups when data are grouped. I tryied to provide a very simple example, with a ajax request to a data.json file to simulate a server response. Just drop the testGrouping.html and data.json files in the root of any http server to reproduce my problem.
When i run the given code, i have no error, but the grid remains empty. I expect the grid to show 1 group with 5 lines, without any aggregate.
Could you help to find what's wrong in the given sample ?
Thank you for your help.
Here is the html page i'm using (testGrouping.html) :
<!DOCTYPE html>
<html>
<head>
<style>html { font-size: 14px; font-family: Arial, Helvetica, sans-serif; }</style>
<title></title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.1.223/styles/kendo.common-material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.1.223/styles/kendo.material.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.1.223/styles/kendo.material.mobile.min.css" />
<script src="https://kendo.cdn.telerik.com/2017.1.223/js/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2017.1.223/js/kendo.all.min.js"> </script>
</head>
<body>
<div id="example">
<div id="grid"></div>
<script>
$(document).ready(function() {
$("#grid").kendoGrid({
dataSource: {
transport: {
read: {
url: "data.json?x=1",
cache:false,
type: 'GET',
dataType: 'json',
contentType: "application/json"
},
},
error: function(e) {
console.log(e.errors); // displays "Invalid query"
},
schema:{
// "data":"titi",
"groups":"groups",
"total": "total",
"model": {
"fields": [
{
"field": "m2"
},
{
"field": "m"
},
]
}
},
pageSize: 7
},
sortable: true,
scrollable: false,
pageable: true,
serverPaging: true,
serverAggregates: true,
serverFiltering: true,
serverGrouping: true,
serverSorting: true,
columns: [
{
"field": "m2",
"title": "Group odd / even"
},
{
"field": "m",
"title": "Month"
}
]
});
});
</script>
</div>
And data used here to simulate server response (data.json) :
{
"total":1,
"groups":
[ {
"aggregates": [],
"value": "rrr",
"hasSubgroups": false,
"field": "m2",
"items": [
{
"m2": "rrr",
"m": 1
},
{
"m2": "rrr",
"m": 1
},
{
"m2": "rrr",
"m": 1
},
{
"m2": "rrr",
"m": 1
},
{
"m2": "rrr",
"m": 1
}
],
"aggregates": {}
}]
}
I've been fighting with this for a couple of days now but I got it working eventually using https://docs.telerik.com/kendo-ui/knowledge-base/grid-format-of-the-response-with-server-grouping and https://docs.telerik.com/kendo-ui/framework/datasource/basic-usage#server-grouping for reference
Set the schema.groups property of the datasource to a function returning the response property that contains the groups
schema: {
groups: function (e) {
return e.Groups;
}
}
Return the following format from your server
Groups = [
{
field: "GroupField",
value: "Group1",
items: [],
aggregates: {},
hasSubgroup: false
},
{
field: "GroupField",
value: "Group2",
items: [],
aggregates: {},
hasSubgroup: false
}
]
I am in trouble trying to load a very simple json where nodes are nested inside "data" as showed below. I expect to get two nodes with text AAA and BBB, but I only get a tree with a couple of empty nodes with infinite children (each children with a single sub children).
test_lrymgr.json
{
"data": [
{
"text": ".",
"children": [
{
"nodename": "AAA",
"isvisible": true,
"expanded": false
},
{
"nodename": "BBB",
"isvisible": true,
"expanded": false
}
]
}
],
"metadata": { "success":true, "msg":"", "totalCount":1, "totalPages":1, "prevLink":"", "nextLink":""}
}
test_lrymgr.html
<html>
<head>
<link rel="stylesheet" type="text/css" href="ext/packages/ext-theme-classic/build/resources/ext-theme-classic-all.css" />
<script type="text/javascript" src="ext/ext-all-debug.js"></script>
</head>
<body>
<script type="text/javascript">
Ext.onReady(function () {
try {
//define model
Ext.define('MyDataModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'nodename', mapping: 'nodename' },
{ name: 'isvisible', mapping: 'isvisible' }
],
});
//define store
Ext.define('MyStore', {
extend: 'Ext.data.TreeStore',
storeId: 'idStoreLryManager',
model: 'MyDataModel',
proxy: {
type: 'ajax',
url: 'test_lrymgr.json',
reader: {
type: 'json',
root: 'data',
metaProperty: 'metadata',
totalProperty: 'metadata.totalCount',
successProperty: 'metadata.success',
messageProperty: 'metadata.msg'
},
}
});
//define tree panel
Ext.define('MyTreePanel', {
extend: 'Ext.tree.Panel',
alias: 'widget.myTreePanel',
store: 'MyStore',
rootVisible: false,
columns: [
{
xtype: 'treecolumn',
dataIndex: 'nodename',
sortable: false,
flex: 2,
header: ''
},
{
xtype: 'checkcolumn',
dataIndex: 'isvisible',
sortable: false,
width: 55,
header: 'Visible'
}
]
});
//create tree view
var storeInstance = Ext.create('MyStore');
Ext.createWidget('window', {
title: 'test',
layout: 'fit',
autoShow: true,
height: 360,
width: 200,
items: {
xtype: 'myTreePanel',
store: storeInstance,
border: false
}
});
//load
storeInstance.load({
scope: storeInstance,
callback: function (records, operation, success) {
try {
}
catch (ex) {
Ext.Msg.alert("", "callback error: " + ex);
}
}
});
}
catch (ex)
{
Ext.Msg.alert("", ex)
}
});
</script>
</body>
</html>
Any idea about what's wrong?...
Your json over here is wrong.Root mentioned in the store is "data".So
your json should be something like this:
{
"data": [{
"nodename": "child1",
"data": [{
"nodename": "subChild1",
"expanded": false,
"leaf": true
}, {
"nodename": "subChild2",
"expanded": false,
"leaf": true
}]
}],
"metadata": {
"success": true,
"msg": "",
"totalCount": 1,
"totalPages": 1,
"prevLink": "",
"nextLink": ""
}
}
For the tree to read nested data, the Ext.data.reader.Reader must be configured with a root property, so the reader can find nested data for each node (if a root is not specified, it will default to 'children'). This will tell the tree to look for any nested tree nodes by the same keyword, i.e., 'children'. If a root is specified in the config make sure that any nested nodes with children have the same name. Note that setting defaultRootProperty accomplishes the same thing.
You can see the simples possible tree configuration here: http://extjs.eu/ext-examples/#tree-simplest
I have a php-script, which returns the following JSON
[
{
"Code":0,
"Message":"No problem"
},
{
"name":"o016561",
"status":1,
"locks":[
{
"ztn":"155320",
"dtn":"20131111",
"idn":"78"
},
{
"ztn":"155320",
"dtn":"20131111",
"idn":"91"
}
]
},
{
"name":"o011111",
"status":1,
"locks":[
{
"ztn":"155320",
"dtn":"20131111",
"idn":"91"
}
]
},
{
"name":"o019999",
"status":0,
"locks":[
]
},
{
"name":"o020000",
"status":0,
"locks":[
]
},
{
"name":"o020001",
"status":0,
"locks":[
]
}
]
Edit:
The grid should look something like this:
I've been able to load name and status into my grid - so far so good. But the more important part is, that I need the nested data in the locks-array being loaded into my grid, but I just can't get my code working. Any help would be appreciated.
I'm using ExtJS 4.2 if that matters.
Edit 2:
I tried
Ext.define("Locks", {
extend: 'Ext.data.Model',
fields: [
'ztn',
'dtn',
'idn'
]
});
Ext.define("ConnectionModel", {
extend: 'Ext.data.Model',
fields: ['name', 'status'],
hasMany: [{
model: 'Locks',
name: 'locks'
}]
});
var store = Ext.create('Ext.data.Store', {
model: "ConnectionModel",
autoLoad: true,
proxy: {
type: 'ajax',
reader: {
type: 'json',
root: 'name'
}
}
});
but it seemed to be wrong in multiple ways...
and it would be awesome if ztn and dtn could be displayed just seperated with a whitespace in the same column
You can add a renderer to the column. In that renderer you can do anything with the record...
Here's a working fiddle:
http://jsfiddle.net/Vandeplas/MWeGa/3/
Ext.create('Ext.grid.Panel', {
title: 'test',
store: store,
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Status',
dataIndex: 'status'
}, {
text: 'idn',
renderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
values = [];
record.locks().each(function(lock){
values.push(lock.get('idn'));
});
return values.join('<br\>');
}
}, {
text: 'ztn + dtn',
renderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
values = [];
record.locks().each(function(lock){
values.push(lock.get('ztn') + ' ' + lock.get('dtn'));
});
return values.join('<br\>');
}
}],
height: 200,
width: 600,
renderTo: Ext.getBody()
});
Note
If you have control over your backend you better change the form of your data more like this:
{
"code": 0,
"message": "No problem",
"success": true,
"data": [
{
"name": "o016561",
"status": 1,
"locks": [
{
"ztn": "155320",
"dtn": "20131111",
"idn": "78"
},
{
"ztn": "155320",
"dtn": "20131111",
"idn": "91"
}
]
},
{
"name": "o011111",
"status": 1,
"locks": [
{
"ztn": "155320",
"dtn": "20131111",
"idn": "91"
}
]
}
]
}
That way you don't mix your control data (success, message, code,...) with your data and the proxy picks it up correctly (it can be a cause of the problems your experiencing). I added a success boolean => Ext picks it up and goes to the failure handler. It helps a lot with your exception handling.
Here is the proxy for it:
proxy: {
type: 'ajax',
api: {
read: ___URL____
},
reader: {
type: 'json',
root: 'data',
messageProperty: 'message'
}
}
i got this jsTree:
$(function () {
$("#tree").jstree({
"json_data" : {
"data" : [
{
"data" : "<?php echo $db_obj->getValue('article_group_name') ?>",
"metadata" : { id : 23 },
"children" : [ "Child 1", "A Child 2" ]
}
]
},
"plugins" : ["themes","json_data", "ui" ]
});
});
I would like to populate it with DB data. The Childs should be line from the database.
I json_encoded the table data, it looks something like this:
[Object { article_id=
"4949"
, article_name_internal=
"Nachtlampe Lumilove Barbapapa"
}, Object { article_id=
"4947"
, article_name_internal=
"Wanduhr Silk von Koziol"
},
Whene i click one of the childs it should go to that page. Not sure how i can populate the tree with this data. Any instructions?
Each node for jsTree have a list of attributes that you can set to it.
just use the attr property in your JSON and add an array of property-value pairs that represent the data you want.
one of these properties should be an href containing the URL for the page you want to opent once someone clicks the node for your jsTree.
now your server should return the data like this.
{
"data": "Root",
"attr": {
"id": "1",
"rel": "Root",
"type": 0
},
"children": [{
"data": "Test 1",
"attr": {
"id": "2",
"href": "http://www.google.com"
"rel": "OrganizationalUnit",
"type": 1
},
"children": [],
"state": "open"
}],
"state": "open"
}
and your JSTree inint function should do something like that:
k_OrgTree = $("#OrgTree").jstree({
json_data: {
ajax: {
url: "/Administration/PopulateTree",
type: "POST",
dataType: "json",
contentType: "application/json charset=utf-8",
success: function (data) { }
}
},
themes: currentTheme,
types: {
valid_children: [k_Root],
types: {
Root: {
valid_children: [k_OrganizationalUnit, k_Calendar],
icon: { image: "/Content/Administration/Images/Root/Root_32x32.png" },
delete_node: false,
},
OrganizationalUnit: {
valid_children: [k_OrganizationalUnit, k_Calendar, k_User],
icon: { image: "/Content/Administration/Images/OrganizationalUnit/OrganizationalUnit_32x32.png" },
},
Calendar: {
valid_children: ["none"],
icon: { image: "/Content/Administration/Images/Calendar/Calendar_32x32.png" },
create_node: false
},
User: {
valid_children: ["none"],
icon: { image: "/Content/Administration/Images/User/User_32x32.png" },
create_node: false
}
}
},
plugins: ["themes", "json_data", "types", "ui"]
});
I am using Kendo UI Line chart from Web Dataviz package for generate a Graph with 500+. but the load of the graph is too lazy. It takes like 25 seconds for the graph to be generated.
I am using a date for Category Axis and a Decimal Value for the serie with a odata datasource.
Can I optimize the load time of the graph?
$("#chart").kendoChart({
theme: $(document).data("kendoSkin") || "default",
dataSource: {
type: "odata",
transport: {
read: crudServiceBaseUrl + "/Odata/TestODataService.svc/EGauges"
},
serverFiltering: true,
serverSorting: true,
sort: { field: "DateData", dir: "asc"},
filter: [
{field: "From", operator: "eq", value: 422 },//400+
{ field: "Id", operator: "eq", value: parseInt(id) },
{ field: "Intervalo", operator: "eq", value: 23 },
{ field: "Tipo", operator: "eq", value: 'm' }
],
title: {
text: ""
},
legend: {
position: "bottom"
},
seriesDefaults: {
type: "area"
},
series: [{
field: "Value",
name: "Value"
}],
categoryAxis: {
field: "DateData",
labels: {
visible: false,
rotation: -90
}
},
axisDefaults: {
visible: true,
majorGridLines: { visible: false }
},
tooltip: {
visible: true
}
});
Here is how the odata service is returning data:
jQuery1704278529312834345_1357310335401({"d" : {
"results": [
{
"__metadata": {
"uri": "http://localhost/Prosol.Web/Odata/TestODataService.svc/EGauges(18)",
"type": "TestOpenErpInterfaz.Web.TestEntityDataSource_EGauge"
},
"EGaugeID": 18,
"From": 422,
"Id": 18,
"Tipo": "m",
"Intervalo": 23,
"DateData": "\/Date(1357310820000)\/",
"Value": "3.72",
"TotalKw": "0",
"TotalCosto": "0.00",
"TotalKwGen": "203.23999999999999999999999999",
"TotalCostoGen": "16.259199999999999999999999999",
"FechaDisplay": "Ene 4, 2013 14:47"
},........
], "__count": "421"
}
})
Finally I Use FlotCharts, For some reason the KendoUI Chart laoad data very lazy... I use Kendo Ui Datasource for read data from Web Service and load on FloatChart...
http://www.flotcharts.org/
This is simple, fast, and freeware