-
Notifications
You must be signed in to change notification settings - Fork 86
Graphviz Example
Jacob Williams edited this page Apr 23, 2017
·
4 revisions
This example shows you how to generate a directed graph of a JSON structure using Graphviz.
Example JSON data from here:
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}
This code reads the above JSON data from a string (note: it could also be read from a file), generates a file in Graphviz dot language, and then uses execute_command_line
to call dot
and generate a png of the graph.
program graphviz_test
use json_module, lk => json_lk, rk => json_rk, ik => json_ik, ck => json_ck
implicit none
character(len=*),parameter :: new_line = char(10)
character(len=*),parameter :: tab = ' ' !! for indenting
type(json_core) :: json
type(json_value),pointer :: p
integer(IK) :: icount,iunit
character(kind=CK,len=:),allocatable :: str
!>
! Example JSON data from: http://json.org/example.html
character(kind=CK,len=*),parameter :: json_example = &
' {'//new_line//&
' "glossary": {'//new_line//&
' "title": "example glossary",'//new_line//&
' "GlossDiv": {'//new_line//&
' "title": "S",'//new_line//&
' "GlossList": {'//new_line//&
' "GlossEntry": {'//new_line//&
' "ID": "SGML",'//new_line//&
' "SortAs": "SGML",'//new_line//&
' "GlossTerm": "Standard Generalized Markup Language",'//new_line//&
' "Acronym": "SGML",'//new_line//&
' "Abbrev": "ISO 8879:1986",'//new_line//&
' "GlossDef": {'//new_line//&
' "para": "A meta-markup language, used to create markup languages such as DocBook.",'//new_line//&
' "GlossSeeAlso": ["GML", "XML"]'//new_line//&
' },'//new_line//&
' "GlossSee": "markup"'//new_line//&
' }'//new_line//&
' }'//new_line//&
' }'//new_line//&
' }'//new_line//&
' }'
! load JSON from string:
call json%parse(p,json_example)
! initialize the graphviz file:
str = ''
str = str // 'digraph json {' // new_line // new_line
str = str // tab // 'graph [ dpi = 300 ]' // new_line // new_line
! traverse the structure:
icount = 0
call graphviz_func(p,icount=icount,ithis=0)
! cleanup:
call json%destroy(p)
! close out the graphviz structure:
str = str // new_line // '}'
! write it to a file:
open(newunit=iunit,file='json.dot',status='REPLACE')
write(iunit,'(A)') str
close(iunit)
! call graphviz to make a png of the graph:
call execute_command_line('dot -Tpng -o json.png json.dot')
contains
recursive subroutine graphviz_func(p,icount,ithis,iparent,iprev)
implicit none
type(json_value),pointer,intent(in) :: p
integer,intent(inout) :: icount !! running counter of all elements
!! traversed (initialize with 0)
integer,intent(in) :: ithis !! index for this element
!! (root should be 0)
integer,intent(in),optional :: iparent !! index for parent
integer,intent(in),optional :: iprev !! index for previous in list
type(json_value),pointer :: child,next
character(len=:),allocatable :: name
integer :: itmp
if (ithis==0) then
!first one (root)
str = str//tab//'0 '//&
'[shape=circle,fillcolor="HoneyDew",style=filled,label="root"]'//&
new_line
end if
itmp = ithis
call json%get_child(p,child)
if (associated(child)) then
icount = icount + 1
name = get_name(child)
!create node and draw arrow from this to child:
str = str//tab//int_to_str(icount)//' '//&
'[shape=circle,fillcolor="cornsilk",style=filled,label="'//&
name//'"]'//new_line
str = str //tab// int_to_str(itmp) // '->' // &
int_to_str(icount)//';'//new_line
!process child:
call graphviz_func(child,icount=icount,&
ithis=icount,iparent=itmp)
end if
call json%get_next(p,next)
if (associated(next)) then
icount = icount + 1
name = get_name(next)
!create node and draw arrow from parent to next:
str = str//tab//int_to_str(icount)//' '//&
'[shape=circle,fillcolor="cornsilk",style=filled,label="'//&
name//'"]'//new_line
str = str //tab// int_to_str(iparent) // '->' //&
int_to_str(icount)//';'//new_line
!process next:
call graphviz_func(next,icount=icount,ithis=icount,&
iparent=iparent,iprev=itmp)
end if
end subroutine graphviz_func
function int_to_str(i) result(str)
!! integer to string
implicit none
integer,intent(in) :: i
character(len=:),allocatable :: str
character(len=10) :: itmp
write(itmp,'(I10)') i
str = trim(adjustl(itmp))
end function int_to_str
function get_name(p) result(name)
!! the name of the node.
!! this is either the name of the json_value variable
!! (if there is one), or the actual value as a string.
implicit none
type(json_value),pointer :: p
character(kind=CK,len=:),allocatable :: name
logical(LK) :: tf
real(RK) :: r
integer(IK) :: i
character(len=30) :: tmp
integer :: var_type
call json%info(p,name=name)
if (name=='') then
call json%info(p,var_type=var_type)
select case (var_type)
case(json_null)
name = 'null'
case(json_logical)
call json%get(p,tf)
if (tf) then
name = 'T'
else
name = 'F'
end if
case(json_string)
call json%get(p,name)
case(json_double)
call json%get(p,r)
write(tmp,'(E30.16)') r
name = trim(adjustl(tmp))
case(json_integer)
call json%get(p,i)
name = int_to_str(i)
case default
name = ''
end select
end if
end function get_name
end program graphviz_test
digraph json {
graph [ dpi = 300 ]
0 [shape=circle,fillcolor="HoneyDew",style=filled,label="root"]
1 [shape=circle,fillcolor="cornsilk",style=filled,label="glossary"]
0->1;
2 [shape=circle,fillcolor="cornsilk",style=filled,label="title"]
1->2;
3 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossDiv"]
1->3;
4 [shape=circle,fillcolor="cornsilk",style=filled,label="title"]
3->4;
5 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossList"]
3->5;
6 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossEntry"]
5->6;
7 [shape=circle,fillcolor="cornsilk",style=filled,label="ID"]
6->7;
8 [shape=circle,fillcolor="cornsilk",style=filled,label="SortAs"]
6->8;
9 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossTerm"]
6->9;
10 [shape=circle,fillcolor="cornsilk",style=filled,label="Acronym"]
6->10;
11 [shape=circle,fillcolor="cornsilk",style=filled,label="Abbrev"]
6->11;
12 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossDef"]
6->12;
13 [shape=circle,fillcolor="cornsilk",style=filled,label="para"]
12->13;
14 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossSeeAlso"]
12->14;
15 [shape=circle,fillcolor="cornsilk",style=filled,label="GML"]
14->15;
16 [shape=circle,fillcolor="cornsilk",style=filled,label="XML"]
14->16;
17 [shape=circle,fillcolor="cornsilk",style=filled,label="GlossSee"]
6->17;
}
- (Modern?) Fortran directed graphs library [comp.lang.fortran]