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