Visualization Guide
Visualization Guide
Transform your sgraph models into beautiful, interactive visualizations for better understanding of your software architecture.
Overview
sgraph supports multiple visualization formats and tools:
| Format | Best For | Interactive | Tool Support |
|---|---|---|---|
| PlantUML | Documentation, reports | No | Many editors |
| Cytoscape.js | Web applications | Yes | Browsers |
| 3D Force Graph | Large networks | Yes | Browsers |
| GraphML | Research, analysis | No | Gephi, yEd |
| DOT/Graphviz | Simple diagrams | No | Graphviz tools |
PlantUML Diagrams
PlantUML creates professional diagrams perfect for documentation.
Basic Usage
from sgraph.converters.xml_to_plantuml import XmlToPlantUml
# Convert model to PlantUML
converter = XmlToPlantUml()
converter.convert('model.xml', 'architecture.puml')
Generated PlantUML Example
@startuml
package "nginx" {
package "src" {
package "core" {
[nginx.c] --> [nginx.h] : includes
[nginx.c] --> [config.h] : includes
}
}
}
@enduml
Customizing PlantUML Output
def create_styled_plantuml(model_path, output_path):
# Generate basic PlantUML
converter = XmlToPlantUml()
converter.convert(model_path, 'temp.puml')
# Read and enhance
with open('temp.puml', 'r') as f:
content = f.read()
# Add custom styling
styled_content = """@startuml
!theme vibrant
skinparam backgroundColor #FAFAFA
skinparam package {
BackgroundColor #E8F4FD
BorderColor #1565C0
FontColor #0D47A1
}
skinparam component {
BackgroundColor #F3E5F5
BorderColor #7B1FA2
FontColor #4A148C
}
skinparam arrow {
Color #1976D2
FontColor #1976D2
}
""" + content.replace("@startuml", "").replace("@enduml", "") + "\n@enduml"
# Save styled version
with open(output_path, 'w') as f:
f.write(styled_content)
# Clean up
os.remove('temp.puml')
# Usage
create_styled_plantuml('model.xml', 'styled_architecture.puml')
Generating Images
# Install PlantUML
npm install -g plantuml
# Generate PNG
plantuml architecture.puml
# Generate SVG (better for web)
plantuml -tsvg architecture.puml
# Generate PDF (for documentation)
plantuml -tpdf architecture.puml
Interactive Web Visualizations
Cytoscape.js Networks
Perfect for detailed, interactive analysis.
from sgraph.converters.sgraph_to_cytoscape import SGraphToCytoscape
# Create interactive network
converter = SGraphToCytoscape()
converter.convert('model.xml', 'network.html')
Features
- Zoom and Pan: Navigate large networks
- Node Selection: Click to highlight dependencies
- Search: Find specific elements
- Filtering: Show/hide element types
- Layouts: Multiple automatic layouts
Custom Cytoscape Styling
// Add to your Cytoscape HTML
var style = [
{
selector: 'node[type="function"]',
style: {
'background-color': '#3498db',
'border-color': '#2980b9',
'label': 'data(name)',
'font-size': '12px'
}
},
{
selector: 'node[type="class"]',
style: {
'background-color': '#e74c3c',
'border-color': '#c0392b',
'shape': 'rectangle'
}
},
{
selector: 'edge[type="calls"]',
style: {
'line-color': '#95a5a6',
'target-arrow-color': '#95a5a6',
'curve-style': 'bezier'
}
}
];
3D Force Graph
Ideal for exploring large, complex networks.
from sgraph.converters.xml_to_3dforcegraph import XmlTo3DForceGraph
# Create 3D visualization
converter = XmlTo3DForceGraph()
converter.convert('large_model.xml', 'force_graph.html')
Features
- 3D Navigation: Rotate, zoom, pan
- Physics Simulation: Natural clustering
- Performance: Handles 10K+ nodes
- Search: Find and highlight nodes
- Filtering: Real-time filtering
Customizing 3D Force Graph
// Configuration options
const Graph = ForceGraph3D()
.nodeAutoColorBy('type')
.nodeThreeObject(node => {
// Custom 3D objects for different types
if (node.type === 'class') {
const geometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshBasicMaterial({ color: 'red' });
return new THREE.Mesh(geometry, material);
}
// Default sphere for other types
return new THREE.Mesh(
new THREE.SphereGeometry(3),
new THREE.MeshBasicMaterial({ color: node.color })
);
})
.linkColor(() => 'rgba(255,255,255,0.2)')
.linkWidth(2);
Professional Graph Tools
Gephi Integration
Gephi is a powerful graph analysis platform.
from sgraph.converters.xml_to_graphml import XmlToGraphMl
# Convert to GraphML for Gephi
converter = XmlToGraphMl()
converter.convert('model.xml', 'network.graphml')
Gephi Workflow
- Import: File → Open → Select
network.graphml - Layout: Choose layout (Force Atlas 2, Fruchterman Reingold)
- Style: Adjust node sizes, colors by attributes
- Filter: Apply filters to focus on subsets
- Export: File → Export → Choose format (PNG, PDF, SVG)
Gephi Tips
- Use Force Atlas 2 for social network-like structures
- Use Yifan Hu for hierarchical structures
- Color nodes by type or module
- Size nodes by degree or importance
yEd Graph Editor
Professional diagramming tool with excellent layouts.
# GraphML works directly with yEd
converter = XmlToGraphMl()
converter.convert('model.xml', 'diagram.graphml')
yEd Features
- Hierarchical Layouts: Perfect for software architecture
- Organic Layouts: Great for dependency networks
- Manual Editing: Fine-tune automatic layouts
- Export Options: High-quality images and PDFs
Command Line Visualization
Quick Graphviz Diagrams
from sgraph.converters.xml_to_dot import XmlToDot
# Generate DOT format
converter = XmlToDot()
converter.convert('model.xml', 'graph.dot')
# Generate images with Graphviz
dot -Tpng graph.dot -o architecture.png
dot -Tsvg graph.dot -o architecture.svg
# Different layouts
circo -Tpng graph.dot -o circular.png # Circular layout
fdp -Tpng graph.dot -o force.png # Force-directed
sfdp -Tpng graph.dot -o large.png # For large graphs
Specialized Visualizations
Dependency Trees
def create_dependency_tree(model_path, root_element_name):
"""Create a tree view of dependencies from a root element"""
from sgraph.modelapi import ModelApi
api = ModelApi(filepath=model_path)
roots = api.getElementsByName(root_element_name)
if not roots:
print(f"Element '{root_element_name}' not found")
return
def print_tree(element, visited=None, indent=0):
if visited is None:
visited = set()
if element.getPath() in visited:
print(" " * indent + f"↻ {element.name} (circular)")
return
visited.add(element.getPath())
print(" " * indent + f"📁 {element.name}")
# Show dependencies
for assoc in element.getAssociationsFrom():
print(" " * (indent + 1) + f"🔗 {assoc.toElement.name} ({assoc.type})")
if indent < 3: # Limit depth
print_tree(assoc.toElement, visited.copy(), indent + 2)
print_tree(roots[0])
# Usage
create_dependency_tree('model.xml', 'main')
Metrics Heatmaps
def create_complexity_heatmap(model_path):
"""Create visualization showing complexity hotspots"""
from sgraph.modelapi import ModelApi
import json
api = ModelApi(filepath=model_path)
elements = api.getAllElements()
# Calculate complexity for each element
complexity_data = []
for element in elements:
fan_out = len(element.getAssociationsFrom())
fan_in = len(element.getAssociationsTo())
complexity = fan_out + fan_in
complexity_data.append({
'id': element.getPath(),
'name': element.name,
'complexity': complexity,
'fan_out': fan_out,
'fan_in': fan_in,
'type': element.getAttribute('type', 'unknown')
})
# Sort by complexity
complexity_data.sort(key=lambda x: x['complexity'], reverse=True)
# Generate HTML heatmap
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Complexity Heatmap</title>
<style>
.heatmap
.element
.high
.medium
.low
</style>
</head>
<body>
<h1>Complexity Heatmap</h1>
<div class="heatmap">
"""
for data in complexity_data[:50]: # Top 50
css_class = 'high' if data['complexity'] > 10 else 'medium' if data['complexity'] > 5 else 'low'
html_content += f"""
<div class="element {css_class}" title="{data['id']} - Complexity: {data['complexity']}">
{data['name']}<br>
<small>{data['complexity']}</small>
</div>
"""
html_content += """
</div>
</body>
</html>
"""
with open('complexity_heatmap.html', 'w') as f:
f.write(html_content)
print("Complexity heatmap created: complexity_heatmap.html")
# Usage
create_complexity_heatmap('model.xml')
Architecture Layer Diagrams
def create_layer_diagram(model_path):
"""Create a layered architecture diagram"""
from sgraph.modelapi import ModelApi
api = ModelApi(filepath=model_path)
elements = api.getAllElements()
# Define layers
layers = {
'presentation': [],
'business': [],
'data': []
}
# Classify elements into layers
for element in elements:
path = element.getPath().lower()
if any(keyword in path for keyword in ['view', 'controller', 'ui']):
layers['presentation'].append(element)
elif any(keyword in path for keyword in ['service', 'business', 'domain']):
layers['business'].append(element)
elif any(keyword in path for keyword in ['repository', 'dao', 'data']):
layers['data'].append(element)
# Generate PlantUML
plantuml_content = """@startuml
!theme vibrant
package "Presentation Layer" {
"""
for element in layers['presentation'][:10]: # Limit for readability
plantuml_content += f" [{element.name}]\n"
plantuml_content += """}
package "Business Layer" {
"""
for element in layers['business'][:10]:
plantuml_content += f" [{element.name}]\n"
plantuml_content += """}
package "Data Layer" {
"""
for element in layers['data'][:10]:
plantuml_content += f" [{element.name}]\n"
plantuml_content += """}
' Add some relationships
"""
# Add cross-layer relationships
for element in layers['presentation'][:5]:
for assoc in element.getAssociationsFrom():
if assoc.toElement in layers['business']:
plantuml_content += f"[{element.name}] --> [{assoc.toElement.name}]\n"
plantuml_content += "\n@enduml"
with open('layers.puml', 'w') as f:
f.write(plantuml_content)
print("Layer diagram created: layers.puml")
# Usage
create_layer_diagram('model.xml')
Performance Considerations
Large Graph Visualization
def optimize_for_visualization(model_path, max_nodes=1000):
"""Optimize large models for visualization"""
from sgraph.modelapi import ModelApi
from sgraph.algorithms.sgraphfiltering import SGraphFiltering
api = ModelApi(filepath=model_path)
# Get most connected elements
elements = api.getAllElements()
element_scores = []
for element in elements:
score = len(element.getAssociationsFrom()) + len(element.getAssociationsTo())
element_scores.append((element, score))
# Sort by connectivity
element_scores.sort(key=lambda x: x[1], reverse=True)
# Take top N most connected elements
important_elements = [elem for elem, score in element_scores[:max_nodes]]
important_paths = {elem.getPath() for elem in important_elements}
# Create filtered model
filtered_model = SGraph(SElement(None, ''))
for element in important_elements:
new_elem = filtered_model.createOrGetElementFromPath(element.getPath())
# Copy attributes
for key, value in element.getAllAttributes().items():
new_elem.addAttribute(key, value)
# Add relationships between important elements
for element in important_elements:
for assoc in element.getAssociationsFrom():
if assoc.toElement.getPath() in important_paths:
from_elem = filtered_model.createOrGetElementFromPath(element.getPath())
to_elem = filtered_model.createOrGetElementFromPath(assoc.toElement.getPath())
new_assoc = SElementAssociation(from_elem, to_elem, assoc.type)
new_assoc.initElems()
# Save optimized model
filtered_model.to_xml('optimized_for_viz.xml')
print(f"Optimized model saved with {len(important_elements)} elements")
# Usage
optimize_for_visualization('huge_model.xml', max_nodes=500)
Integration Examples
Jupyter Notebook Integration
# In Jupyter notebook
from IPython.display import HTML, display
from sgraph.converters.sgraph_to_cytoscape import SGraphToCytoscape
def show_interactive_graph(model_path):
"""Display interactive graph in Jupyter"""
converter = SGraphToCytoscape()
# Generate minimal HTML for embedding
html_content = converter.generate_embed_html(model_path)
display(HTML(html_content))
# Usage in notebook
show_interactive_graph('model.xml')
Web Application Integration
// React component example
import React, { useEffect, useRef } from 'react';
import cytoscape from 'cytoscape';
function ArchitectureViewer({ modelData }) {
const containerRef = useRef(null);
useEffect(() => {
const cy = cytoscape({
container: containerRef.current,
elements: modelData,
style: [
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(name)',
'font-size': '12px'
}
}
],
layout: { name: 'cose' }
});
return () => cy.destroy();
}, [modelData]);
return <div ref={containerRef} style= />;
}
Best Practices
Choosing Visualization Types
Use PlantUML for:
- 📄 Documentation and reports
- 🎯 Focused, clean diagrams
- 📋 Architecture overviews
Use Cytoscape.js for:
- 🌐 Interactive web applications
- 🔍 Detailed analysis interfaces
- 🎮 User-driven exploration
Use 3D Force Graph for:
- 🚀 Large datasets (1000+ nodes)
- 🎪 Impressive demonstrations
- 🔮 Pattern discovery in complex networks
Use Gephi/yEd for:
- 📊 Research and analysis
- 🎨 Publication-quality images
- 🔧 Fine-tuned manual layouts
Color Coding Strategies
# Consistent color scheme
COLOR_SCHEME = {
'function': '#3498db', # Blue
'class': '#e74c3c', # Red
'module': '#2ecc71', # Green
'package': '#9b59b6', # Purple
'interface': '#f39c12', # Orange
'test': '#95a5a6' # Gray
}
def apply_color_coding(element):
element_type = element.getAttribute('type', 'unknown')
return COLOR_SCHEME.get(element_type, '#34495e')
Performance Tips
- Limit Node Count: Cap visualizations at 1000-2000 nodes
- Filter Strategically: Show only relevant relationships
- Use Clustering: Group related elements
- Progressive Loading: Load details on demand
- Cache Results: Save processed visualizations
Ready to create stunning visualizations of your software architecture! 🎨📊