1 /** \file
2 * \brief Implements GEXF write functionality of class GraphIO.
3 *
4 * \author Łukasz Hanuszczak, Tilo Wiedera
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32 #include <ogdf/fileformats/GraphIO.h>
33 #include <ogdf/fileformats/GEXF.h>
34 #include <ogdf/fileformats/GraphML.h>
35 #include <ogdf/lib/pugixml/pugixml.h>
36
37
38 namespace ogdf {
39
40 namespace gexf {
41
42
writeHeader(pugi::xml_document & doc,bool viz)43 static inline pugi::xml_node writeHeader(pugi::xml_document &doc, bool viz)
44 {
45 pugi::xml_node rootNode = doc.append_child("gexf");
46 rootNode.append_attribute("version") = "1.2";
47 rootNode.append_attribute("xmlns") = "http://www.gexf.net/1.2draft";
48
49 if(viz) {
50 rootNode.append_attribute("xmlns:viz") = "http://www.gexf.net/1.2draft/viz";
51 }
52
53 // TODO: creator, description and date information.
54
55 return rootNode;
56 }
57
58
59 template <typename T>
writeAttValue(pugi::xml_node xmlNode,const graphml::Attribute & attr,const T & value)60 static inline void writeAttValue(
61 pugi::xml_node xmlNode,
62 const graphml::Attribute &attr,
63 const T &value)
64 {
65 pugi::xml_node child = xmlNode.append_child("attvalue");
66 child.append_attribute("for") = graphml::toString(attr).c_str();
67 child.append_attribute("value") = value;
68 }
69
70
defineAttribute(pugi::xml_node xmlNode,const std::string & name,const std::string & type)71 static inline void defineAttribute(
72 pugi::xml_node xmlNode,
73 const std::string &name,
74 const std::string &type)
75 {
76 pugi::xml_node child = xmlNode.append_child("attribute");
77 child.append_attribute("id") = name.c_str();
78 child.append_attribute("title") = name.c_str();
79 child.append_attribute("type") = type.c_str();
80 }
81
82
defineAttributes(pugi::xml_node xmlNode,const GraphAttributes & GA)83 static inline void defineAttributes(
84 pugi::xml_node xmlNode,
85 const GraphAttributes &GA)
86 {
87 const long attrs = GA.attributes();
88
89 // Declare node attributes.
90 pugi::xml_node child = xmlNode.append_child("attributes");
91 child.append_attribute("class") = "node";
92
93 if(attrs & GraphAttributes::nodeId) {
94 defineAttribute(child, graphml::toString(graphml::Attribute::NodeId), "int");
95 }
96
97 if(attrs & GraphAttributes::nodeType) {
98 defineAttribute(child, graphml::toString(graphml::Attribute::NodeType), "string");
99 }
100
101 if(attrs & GraphAttributes::nodeTemplate) {
102 defineAttribute(child, graphml::toString(graphml::Attribute::Template), "string");
103 }
104
105 if(attrs & GraphAttributes::nodeWeight) {
106 defineAttribute(child, graphml::toString(graphml::Attribute::NodeWeight), "float");
107 }
108
109 if(attrs & GraphAttributes::nodeStyle) {
110 defineAttribute(child, graphml::toString(graphml::Attribute::NodeStrokeColor), "string");
111 defineAttribute(child, graphml::toString(graphml::Attribute::NodeStrokeType), "string");
112 defineAttribute(child, graphml::toString(graphml::Attribute::NodeStrokeWidth), "float");
113 defineAttribute(child, graphml::toString(graphml::Attribute::NodeFillPattern), "string");
114 defineAttribute(child, graphml::toString(graphml::Attribute::NodeFillBackground), "string");
115 }
116
117 if(attrs & GraphAttributes::nodeLabelPosition) {
118 defineAttribute(child, graphml::toString(graphml::Attribute::NodeLabelX), "float");
119 defineAttribute(child, graphml::toString(graphml::Attribute::NodeLabelY), "float");
120 if(attrs & GraphAttributes::threeD) {
121 defineAttribute(child, graphml::toString(graphml::Attribute::NodeLabelZ), "float");
122 }
123 }
124
125 // Declare edge attributes.
126 child = xmlNode.append_child("attributes");
127 child.append_attribute("class") = "edge";
128
129 if(attrs & GraphAttributes::edgeType) {
130 defineAttribute(child, graphml::toString(graphml::Attribute::EdgeType), "string");
131 }
132
133 if(attrs & GraphAttributes::edgeArrow) {
134 defineAttribute(child, graphml::toString(graphml::Attribute::EdgeArrow), "string");
135 }
136
137 if(attrs & GraphAttributes::edgeGraphics) {
138 defineAttribute(child, graphml::toString(graphml::Attribute::EdgeBends), "string");
139 }
140
141 if(attrs & GraphAttributes::edgeSubGraphs) {
142 defineAttribute(child, graphml::toString(graphml::Attribute::EdgeSubGraph), "string");
143 }
144 }
145
146
writeColor(pugi::xml_node xmlNode,const Color color)147 static inline void writeColor(pugi::xml_node xmlNode, const Color color)
148 {
149 pugi::xml_node child = xmlNode.append_child("viz:color");
150 child.append_attribute("red") = color.red();
151 child.append_attribute("green") = color.green();
152 child.append_attribute("blue") = color.blue();
153 child.append_attribute("alpha") = color.alpha();
154 }
155
156
writeAttributes(pugi::xml_node xmlNode,const GraphAttributes & GA,node v)157 static inline void writeAttributes(
158 pugi::xml_node xmlNode,
159 const GraphAttributes &GA,
160 node v)
161 {
162 const long attrs = GA.attributes();
163
164 if(attrs & GraphAttributes::nodeGraphics) {
165 pugi::xml_node child = xmlNode.append_child("viz:position");
166 child.append_attribute("x") = GA.x(v);
167 child.append_attribute("y") = GA.y(v);
168 if (attrs & GraphAttributes::threeD) {
169 child.append_attribute("z") = GA.z(v);
170 }
171
172 const double size = GA.width(v) / LayoutStandards::defaultNodeWidth();
173 if(GA.weight(v) / LayoutStandards::defaultNodeWidth() != GA.height(v) / LayoutStandards::defaultNodeHeight()) {
174 GraphIO::logger.lout() << "height and width of " << v->index() << " are not equal!\n";
175 }
176 xmlNode.append_child("viz:size").append_attribute("value") = size;
177
178 const Shape shape = GA.shape(v);
179 xmlNode.append_child("viz:shape").append_attribute("value") = toString(shape).c_str();
180 }
181
182 if(attrs & GraphAttributes::nodeStyle) {
183 writeColor(xmlNode, GA.fillColor(v));
184 }
185
186 /*
187 * Node type, template and weight are not supported by VIZ module. So, they
188 * need to be written using <attvalues> tag (for estetic reasons, we write
189 * them only if either of them is present). For convenience reasons, we use
190 * the same names and values as in GraphML format.
191 */
192 if(!(attrs & (GraphAttributes::nodeId
193 | GraphAttributes::nodeType
194 | GraphAttributes::nodeTemplate
195 | GraphAttributes::nodeWeight
196 | GraphAttributes::nodeStyle))) {
197 return;
198 }
199
200 pugi::xml_node attValues = xmlNode.append_child("attvalues");
201
202 if(attrs & GraphAttributes::nodeId) {
203 writeAttValue(attValues, graphml::Attribute::NodeId, GA.idNode(v));
204 }
205
206 if(attrs & GraphAttributes::nodeType) {
207 writeAttValue(attValues, graphml::Attribute::NodeType, graphml::toString(GA.type(v)).c_str());
208 }
209
210 if(attrs & GraphAttributes::nodeTemplate) {
211 writeAttValue(attValues, graphml::Attribute::Template, GA.templateNode(v).c_str());
212 }
213
214 if(attrs & GraphAttributes::nodeWeight) {
215 writeAttValue(attValues, graphml::Attribute::NodeWeight, GA.weight(v));
216 }
217
218 if(attrs & GraphAttributes::nodeStyle) {
219 writeAttValue(attValues, graphml::Attribute::NodeStrokeColor, GA.strokeColor(v).toString().c_str());
220 writeAttValue(attValues, graphml::Attribute::NodeStrokeWidth, GA.strokeWidth(v));
221 writeAttValue(attValues, graphml::Attribute::NodeStrokeType, toString(GA.strokeType(v)).c_str());
222 writeAttValue(attValues, graphml::Attribute::NodeFillPattern, toString(GA.fillPattern(v)).c_str());
223 writeAttValue(attValues, graphml::Attribute::NodeFillBackground, GA.fillBgColor(v).toString().c_str());
224 }
225
226 if(attrs & GraphAttributes::nodeLabelPosition) {
227 writeAttValue(attValues, graphml::Attribute::NodeLabelX, GA.xLabel(v));
228 writeAttValue(attValues, graphml::Attribute::NodeLabelY, GA.yLabel(v));
229 if(attrs & GraphAttributes::threeD) {
230 writeAttValue(attValues, graphml::Attribute::NodeLabelZ, GA.zLabel(v));
231 }
232 }
233 }
234
235
writeAttributes(pugi::xml_node xmlNode,const GraphAttributes & GA,edge e)236 static inline void writeAttributes(
237 pugi::xml_node xmlNode,
238 const GraphAttributes &GA,
239 edge e)
240 {
241 const long attrs = GA.attributes();
242
243 if(attrs & GraphAttributes::edgeStyle) {
244 writeColor(xmlNode, GA.strokeColor(e));
245 xmlNode.append_child("viz:thickness").append_attribute("value") = GA.strokeWidth(e);
246 xmlNode.append_child("viz:shape").append_attribute("value") = toGEXFStrokeType(GA.strokeType(e)).c_str();
247 }
248
249 /*
250 * The following attributes are not supported by VIZ module. Therefore, they
251 * need to be written using <attvalues> tag (for aesthetic reasons, we write
252 * them only if any of them is present). For convenience reasons, we use the
253 * same names and values as in GraphML format.
254 */
255 if(!(attrs & (GraphAttributes::edgeType | GraphAttributes::edgeArrow | GraphAttributes::edgeGraphics | GraphAttributes::edgeSubGraphs))) {
256 return;
257 }
258
259 pugi::xml_node attValues = xmlNode.append_child("attvalues");
260
261 if(attrs & GraphAttributes::edgeType) {
262 writeAttValue(attValues, graphml::Attribute::EdgeType, graphml::toString(GA.type(e)).c_str());
263 }
264 if(attrs & GraphAttributes::edgeArrow) {
265 writeAttValue(attValues, graphml::Attribute::EdgeArrow, graphml::toString(GA.arrowType(e)).c_str());
266 }
267 if(attrs & GraphAttributes::edgeGraphics && !GA.bends(e).empty()) {
268 std::stringstream sstream;
269 sstream.setf(std::ios::fixed);
270
271 for(const DPoint &p : GA.bends(e)) {
272 sstream << p.m_x << " " << p.m_y << " ";
273 }
274
275 writeAttValue(attValues, graphml::Attribute::EdgeBends, sstream.str().c_str());
276 }
277 if(attrs & GraphAttributes::edgeSubGraphs) {
278 const uint32_t mask = GA.subGraphBits(e);
279
280 // Iterate over all subgraphs and print the ones the edge is part of.
281 std::stringstream sstream;
282 for (size_t sg = 0; sg < sizeof(mask) * 8; ++sg) {
283 if((1 << sg) & mask) {
284 sstream << (sg == 0 ? "" : " ") << sg;
285 }
286 }
287 writeAttValue(attValues, graphml::Attribute::EdgeSubGraph, sstream.str().c_str());
288 }
289 }
290
291
writeNode(pugi::xml_node xmlNode,const GraphAttributes * GA,node v)292 static inline void writeNode(
293 pugi::xml_node xmlNode,
294 const GraphAttributes *GA,
295 node v)
296 {
297 pugi::xml_node nodeTag = xmlNode.append_child("node");
298 nodeTag.append_attribute("id") = v->index();
299
300 if(GA) {
301 if(GA->has(GraphAttributes::nodeLabel)) {
302 nodeTag.append_attribute("label") = GA->label(v).c_str();
303 }
304
305 writeAttributes(nodeTag, *GA, v);
306 }
307 }
308
309
writeEdge(pugi::xml_node xmlNode,const GraphAttributes * GA,edge e)310 static inline void writeEdge(
311 pugi::xml_node xmlNode,
312 const GraphAttributes *GA,
313 edge e)
314 {
315 pugi::xml_node edge = xmlNode.append_child("edge");
316 edge.append_attribute("id") = e->index();
317
318 edge.append_attribute("source") = e->source()->index();
319 edge.append_attribute("target") = e->target()->index();
320
321 if(GA) {
322 if(GA->has(GraphAttributes::edgeLabel)) {
323 edge.append_attribute("label") = GA->label(e).c_str();
324 }
325 if(GA->has(GraphAttributes::edgeDoubleWeight)) {
326 edge.append_attribute("weight") = GA->doubleWeight(e);
327 } else if(GA->has(GraphAttributes::edgeIntWeight)) {
328 // GEXF requires the weight field to be floating point.
329 edge.append_attribute("weight") = double(GA->intWeight(e));
330 }
331
332 writeAttributes(edge, *GA, e);
333 }
334 }
335
336
writeEdges(pugi::xml_node xmlNode,const Graph & G,const GraphAttributes * GA)337 static inline void writeEdges(
338 pugi::xml_node xmlNode,
339 const Graph &G,
340 const GraphAttributes *GA)
341 {
342 pugi::xml_node edges = xmlNode.append_child("edges");
343
344 for(edge e : G.edges) {
345 writeEdge(edges, GA, e);
346 }
347 }
348
349
writeCluster(pugi::xml_node rootNode,const ClusterGraph & C,const ClusterGraphAttributes * CA,cluster c)350 static void writeCluster(
351 pugi::xml_node rootNode,
352 const ClusterGraph &C,
353 const ClusterGraphAttributes *CA,
354 cluster c)
355 {
356 pugi::xml_node graph;
357
358 if(C.rootCluster() != c) {
359 graph = rootNode.append_child("node");
360 graph.append_attribute("id") = ("cluster" + to_string(c->index())).c_str();
361 } else {
362 graph = rootNode.append_child("graph");
363 graph.append_attribute("mode") = "static";
364 graph.append_attribute("defaultedgetype") = CA && !CA->directed() ? "undirected" : "directed";
365
366 if(CA) {
367 defineAttributes(graph, *CA);
368 }
369 }
370
371 pugi::xml_node nodes = graph.append_child("nodes");
372
373 for(cluster child : c->children) {
374 writeCluster(nodes, C, CA, child);
375 }
376
377 for(node v : c->nodes) {
378 writeNode(nodes, CA, v);
379 }
380
381 if(C.rootCluster() == c) {
382 writeEdges(graph, C.constGraph(), CA);
383 }
384 }
385
386
writeGraph(pugi::xml_node rootNode,const Graph & G,const GraphAttributes * GA)387 static void writeGraph(
388 pugi::xml_node rootNode,
389 const Graph &G,
390 const GraphAttributes *GA)
391 {
392 pugi::xml_node graph = rootNode.append_child("graph");
393 graph.append_attribute("mode") = "static";
394 graph.append_attribute("defaultedgetype") = GA && !GA->directed() ? "undirected" : "directed";
395
396 if(GA) {
397 defineAttributes(graph, *GA);
398 }
399
400 pugi::xml_node nodes = graph.append_child("nodes");
401
402 for(node v : G.nodes) {
403 writeNode(nodes, GA, v);
404 }
405
406 gexf::writeEdges(graph, G, GA);
407 }
408
409 }
410
writeGEXF(const Graph & G,std::ostream & out)411 bool GraphIO::writeGEXF(const Graph &G, std::ostream &out)
412 {
413 bool result = out.good();
414
415 if(result) {
416 pugi::xml_document doc;
417 pugi::xml_node rootNode = gexf::writeHeader(doc, false);
418 gexf::writeGraph(rootNode, G, nullptr);
419 doc.save(out);
420 }
421
422 return result;
423 }
424
425
writeGEXF(const ClusterGraph & C,std::ostream & out)426 bool GraphIO::writeGEXF(const ClusterGraph &C, std::ostream &out)
427 {
428 bool result = out.good();
429
430 if(result) {
431 pugi::xml_document doc;
432 pugi::xml_node rootNode = gexf::writeHeader(doc, false);
433 gexf::writeCluster(rootNode, C, nullptr, C.rootCluster());
434 doc.save(out);
435
436 return true;
437 }
438
439 return result;
440 }
441
442
writeGEXF(const GraphAttributes & GA,std::ostream & out)443 bool GraphIO::writeGEXF(const GraphAttributes &GA, std::ostream &out)
444 {
445 bool result = out.good();
446
447 if(result) {
448 pugi::xml_document doc;
449 pugi::xml_node rootNode = gexf::writeHeader(doc, true);
450 gexf::writeGraph(rootNode, GA.constGraph(), &GA);
451 doc.save(out);
452
453 return true;
454 }
455
456 return result;
457 }
458
459
writeGEXF(const ClusterGraphAttributes & CA,std::ostream & out)460 bool GraphIO::writeGEXF(const ClusterGraphAttributes &CA, std::ostream &out)
461 {
462 bool result = out.good();
463
464 if(result) {
465 const ClusterGraph &C = CA.constClusterGraph();
466
467 pugi::xml_document doc;
468 pugi::xml_node rootNode = gexf::writeHeader(doc, true);
469 gexf::writeCluster(rootNode, C, &CA, C.rootCluster());
470 doc.save(out);
471
472 return true;
473 }
474
475 return result;
476 }
477
478 }
479