From a972089fecb01c62880c6a2a5bc5cbcb96105580 Mon Sep 17 00:00:00 2001 From: franck cuny Date: Mon, 13 Jun 2011 18:38:56 +0200 Subject: add Graph::GEXF while it's not on CPAN Signed-off-by: franck cuny --- lib/Graph/GEXF.pm | 177 ++++++++++++++++++++++++++++++++++++ lib/Graph/GEXF/Attribute.pm | 16 ++++ lib/Graph/GEXF/Edge.pm | 24 +++++ lib/Graph/GEXF/Node.pm | 155 +++++++++++++++++++++++++++++++ lib/Graph/GEXF/Role/Attributes.pm | 60 ++++++++++++ lib/Graph/GEXF/Role/Viz/Color.pm | 32 +++++++ lib/Graph/GEXF/Role/Viz/Position.pm | 18 ++++ lib/Graph/GEXF/Role/Viz/Shape.pm | 39 ++++++++ lib/Graph/GEXF/Role/Viz/Size.pm | 21 +++++ lib/Graph/GEXF/Role/XML.pm | 168 ++++++++++++++++++++++++++++++++++ 10 files changed, 710 insertions(+) create mode 100644 lib/Graph/GEXF.pm create mode 100644 lib/Graph/GEXF/Attribute.pm create mode 100644 lib/Graph/GEXF/Edge.pm create mode 100644 lib/Graph/GEXF/Node.pm create mode 100644 lib/Graph/GEXF/Role/Attributes.pm create mode 100644 lib/Graph/GEXF/Role/Viz/Color.pm create mode 100644 lib/Graph/GEXF/Role/Viz/Position.pm create mode 100644 lib/Graph/GEXF/Role/Viz/Shape.pm create mode 100644 lib/Graph/GEXF/Role/Viz/Size.pm create mode 100644 lib/Graph/GEXF/Role/XML.pm diff --git a/lib/Graph/GEXF.pm b/lib/Graph/GEXF.pm new file mode 100644 index 0000000..f226b06 --- /dev/null +++ b/lib/Graph/GEXF.pm @@ -0,0 +1,177 @@ +package Graph::GEXF; + +# ABSTRACT: Manipulate graph file in GEXF + +use Moose; + +use Data::UUID::LibUUID; +use Moose::Util::TypeConstraints; + +use Graph::GEXF::Node; + +with + 'Graph::GEXF::Role::XML', + 'Graph::GEXF::Role::Attributes' => + { for => [qw/node edge/], with_method => 1 }; + +=attr visualization (B) + +if set to true, the generated graph will includes visualizations informations + +=cut + +has visualization => ( + is => 'ro', + isa => 'Bool', + predicate => 'has_visualization', +); + +=attr graph_mode (B) + +Is your graph static or dynamic. + +=cut + +has graph_mode => ( + is => 'ro', + isa => enum( [qw/static dynamic/] ), + required => 1, + default => 'static', +); + +=attr edge_type (B) + +The type of the edges + +=cut + +has edge_type => ( + is => 'ro', + isa => enum( [qw/directed undirected mutual notset/] ), + required => 1, + default => 'directed', +); + +=attr nodes + +a HashRef of L objects. + +=cut + +=method total_nodes + +Return the list of nodes attached to the graph + +=cut + +=method get_node + +Return a node + +=cut + +=method add_node_attribute($name, $type, [$default_value]) + +Add attributes to node + +=method all_nodes + +Return all the nodes + +=cut + +has nodes => ( + traits => ['Hash'], + is => 'rw', + isa => 'HashRef[Graph::GEXF::Node]', + default => sub { {} }, + auto_deref => 1, + handles => { + _node_exists => 'exists', + _add_node => 'set', + total_nodes => 'count', + get_node => 'get', + all_nodes => 'keys', + }, +); + +=method add_node + +Add a new node to the graph + +=cut + +sub add_node { + my $self = shift; + my ($id, %attributes); + + # TODO should be possible to add a Graph::GEXF::Node too + + if ( @_ == 1 ) { + $id = shift; + } + else { + if ( ( @_ % 2 ) == 0 ) { + %attributes = @_; + } + else { + $id = shift; + %attributes = @_; + } + } + + if ($id && $self->_node_exists($id)) { + die "Can't add node wih id $id: already exists"; + } + + $id = new_uuid_string() if !defined $id; + + my $node = Graph::GEXF::Node->new(id => $id); + + map { + my $attribute = $self->get_node_attribute($_); + $node->set_node_attribute( + $_ => { + id => $attribute->{id}, + name => $attribute->{name}, + type => $attribute->{type}, + } + ); + } $self->attributes_node_list; + + $self->_add_node($id => $node); + $node; +} + +1; + +=head1 SYNOPSIS + + # create a new graph + my $graph = Graph::GEXF->new(); + + # add some attributes for nodes + $graph->add_node_attribute('url', 'string'); + + # create a new node and set the label + my $n1 = $graph->add_node(0); + $n1->label('Gephi'); + + my $n2 = $graph->add_node(1); + $n2->label('WebAtlas'); + + my $n3 = $graph->add_node(2); + $n3->label('RTGI'); + + # create relations between nodes + $n1->link_to(1, 2); + $n2->link_to(0); + $n3->link_to(1); + + # set the value for attributes + $n1->attribute('url' => 'http://gephi.org/'); + $n2->attribute('url' => 'http://webatlas.fr/'); + $n3->attribute('url' => 'http://rtgi.fr/'); + + # render the graph in XML + my $xml = $graph->to_xml; diff --git a/lib/Graph/GEXF/Attribute.pm b/lib/Graph/GEXF/Attribute.pm new file mode 100644 index 0000000..3360307 --- /dev/null +++ b/lib/Graph/GEXF/Attribute.pm @@ -0,0 +1,16 @@ +package Graph::GEXF::Attribute; + +use Moose; + +has id => (is => 'ro', isa => 'Int', required => 1,); +has title => (is => 'rw', isa => 'Str'); +has value => (is => 'rw', isa => 'Str'); +has type => ( + is => 'ro', + isa => enum([qw/string integer float double boolean date anyURI/]) +); + +no Moose; + +1; + diff --git a/lib/Graph/GEXF/Edge.pm b/lib/Graph/GEXF/Edge.pm new file mode 100644 index 0000000..a116a01 --- /dev/null +++ b/lib/Graph/GEXF/Edge.pm @@ -0,0 +1,24 @@ +package Graph::GEXF::Edge; + +use Moose; +use Data::UUID::LibUUID; + +with + 'Graph::GEXF::Role::Viz::Size' => { as => 'thickness' }, + 'Graph::GEXF::Role::Viz::Shape' => { for => 'edge' }; + +has id => ( + is => 'ro', + isa => 'Str', + required => 1, + default => sub { new_uuid_string() } +); + +has source => (is => 'ro', isa => 'Str', required => 1); +has target => (is => 'ro', isa => 'Str', required => 1); +has label => (is => 'rw', isa => 'Str'); +has weight => (is => 'rw', isa => 'Num', lazy => 1, default => 1); + +no Moose; + +1; diff --git a/lib/Graph/GEXF/Node.pm b/lib/Graph/GEXF/Node.pm new file mode 100644 index 0000000..8f8187a --- /dev/null +++ b/lib/Graph/GEXF/Node.pm @@ -0,0 +1,155 @@ +package Graph::GEXF::Node; + +use Moose; +use Graph::GEXF::Edge; + +with + 'Graph::GEXF::Role::Attributes' => { for => [qw/node/] }, + 'Graph::GEXF::Role::Viz::Color', 'Graph::GEXF::Role::Viz::Position', + 'Graph::GEXF::Role::Viz::Size' => { as => 'size' }, + 'Graph::GEXF::Role::Viz::Shape' => { for => 'node' }; + +has id => ( + is => 'ro', + isa => 'Str', + required => 1, + traits => ['Chained'] +); + +has label => ( + is => 'rw', + isa => 'Str', + traits => ['Chained'] +); + +has edges => ( + traits => ['Hash'], + is => 'rw', + isa => 'HashRef[Graph::GEXF::Edge]', + default => sub { {} }, + handles => { + add_edge => 'set', + has_link_to => 'exists', + all_edges => 'keys', + get_edge => 'get', + } +); + +sub link_to { + my $self = shift; + my @nodes_id = @_; + + foreach my $node_id (@nodes_id) { + my $edge; + if ( ref $node_id ) { + $edge = Graph::GEXF::Edge->new( + source => $self->id, + target => $node_id->{target}, + weight => $node_id->{weight} + ); + $self->add_edge( $node_id->{target} => $edge ); + } + else { + $edge = Graph::GEXF::Edge->new( + source => $self->id, + target => $node_id, + ); + $self->add_edge( $node_id => $edge ); + } + } +} + +sub attribute { + my ($self, $attribute_name, $value) = @_; + +# return 0 unless $self->has_node_attribute; + + if (!$self->has_node_attribute($attribute_name)) { + die "this attribute doesn't exists"; + } + + $self->node_attributes->{$attribute_name}->{value} = $value; + + 1; +} + +no Moose; + +1; + +=head1 SYNOPSIS + + my $graph = Graph::GEXF->new(); + + my $n = $graph->add_node(); + +=head1 DESCRIPTION + +=head2 ATTRIBUTES + +=head3 id + + my $n = $graph->add_node(1); + $n->id; # returns 1 + +The B of a node can't be changed once the node is created. + +=head3 label + + $n->label('franckcuny'); + $n->label(); + +Each node has a label. If the B