Module Graph
Expand source code
# Copyright (C) 2024
# Wassim Jabi <wassim.jabi@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with
# this program. If not, see <https://www.gnu.org/licenses/>.
import topologic_core as topologic
import random
import time
import os
import warnings
try:
import numpy as np
except:
print("Graph - Installing required numpy library.")
try:
os.system("pip install numpy")
except:
os.system("pip install numpy --user")
try:
import numpy as np
print("Graph - numpy library installed correctly.")
except:
warnings.warn("Graph - Error: Could not import numpy.")
try:
import pandas as pd
except:
print("Graph - Installing required pandas library.")
try:
os.system("pip install pandas")
except:
os.system("pip install pandas --user")
try:
import pandas as pd
print("Graph - pandas library installed correctly.")
except:
warnings.warn("Graph - Error: Could not import pandas.")
try:
from tqdm.auto import tqdm
except:
print("Graph - Installing required tqdm library.")
try:
os.system("pip install tqdm")
except:
os.system("pip install tqdm --user")
try:
from tqdm.auto import tqdm
print("Graph - tqdm library installed correctly.")
except:
warnings.warn("Graph - Error: Could not import tqdm.")
class _Tree:
def __init__(self, node="", *children):
self.node = node
self.width = len(node)
if children:
self.children = children
else:
self.children = []
def __str__(self):
return "%s" % (self.node)
def __repr__(self):
return "%s" % (self.node)
def __getitem__(self, key):
if isinstance(key, int) or isinstance(key, slice):
return self.children[key]
if isinstance(key, str):
for child in self.children:
if child.node == key:
return child
def __iter__(self):
return self.children.__iter__()
def __len__(self):
return len(self.children)
class _DrawTree(object):
def __init__(self, tree, parent=None, depth=0, number=1):
self.x = -1.0
self.y = depth
self.tree = tree
self.children = [
_DrawTree(c, self, depth + 1, i + 1) for i, c in enumerate(tree.children)
]
self.parent = parent
self.thread = None
self.mod = 0
self.ancestor = self
self.change = self.shift = 0
self._lmost_sibling = None
# this is the number of the node in its group of siblings 1..n
self.number = number
def left(self):
return self.thread or len(self.children) and self.children[0]
def right(self):
return self.thread or len(self.children) and self.children[-1]
def lbrother(self):
n = None
if self.parent:
for node in self.parent.children:
if node == self:
return n
else:
n = node
return n
def get_lmost_sibling(self):
if not self._lmost_sibling and self.parent and self != self.parent.children[0]:
self._lmost_sibling = self.parent.children[0]
return self._lmost_sibling
lmost_sibling = property(get_lmost_sibling)
def __str__(self):
return "%s: x=%s mod=%s" % (self.tree, self.x, self.mod)
def __repr__(self):
return self.__str__()
class Graph:
@staticmethod
def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, tolerance=0.0001):
"""
Returns the adjacency matrix of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_matrix.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexKey : str , optional
If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None.
reverse : bool , optional
If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
edgeKeyFwd : str , optional
If set, the value at this key in the connecting edge from start vertex to end verrtex (forward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting.
edgeKeyBwd : str , optional
If set, the value at this key in the connecting edge from end vertex to start verrtex (backward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting.
bidirKey : bool , optional
If set to True or False, this key in the connecting edge will be used to determine is the edge is supposed to be bidirectional or not. If set to None, the input variable bidrectional will be used instead. The default is None
bidirectional : bool , optional
If set to True, the edges in the graph that do not have a bidireKey in their dictionaries will be treated as being bidirectional. Otherwise, the start vertex and end vertex of the connecting edge will determine the direction. The default is True.
useEdgeIndex : bool , False
If set to True, the adjacency matrix values will the index of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
useEdgeLength : bool , False
If set to True, the adjacency matrix values will the length of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The adjacency matrix.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
from topologicpy.Helper import Helper
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AdjacencyMatrix - Error: The input graph is not a valid graph. Returning None.")
return None
vertices = Graph.Vertices(graph)
if not vertexKey == None:
sorting_values = []
for v in vertices:
d = Topology.Dictionary(v)
value = Dictionary.ValueAtKey(d, vertexKey)
sorting_values.append(value)
vertices = Helper.Sort(vertices, sorting_values)
if reverse == True:
vertices.reverse()
edges = Graph.Edges(graph)
order = len(vertices)
matrix = []
# Initialize the matrix with zeroes
for i in range(order):
tempRow = []
for j in range(order):
tempRow.append(0)
matrix.append(tempRow)
for i, edge in enumerate(edges):
sv = Edge.StartVertex(edge)
ev = Edge.EndVertex(edge)
svi = Vertex.Index(sv, vertices, tolerance=tolerance)
evi = Vertex.Index(ev, vertices, tolerance=tolerance)
if bidirKey == None:
bidir = bidirectional
else:
bidir = Dictionary.ValueAtKey(Topology.Dictionary(edge), bidirKey)
if bidir == None:
bidir = bidirectional
if edgeKeyFwd == None:
valueFwd = 1
else:
valueFwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyFwd)
if valueFwd == None:
valueFwd = 1
if edgeKeyBwd == None:
valueBwd = 1
else:
valueBwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyBwd)
if valueBwd == None:
valueBwd = 1
if useEdgeIndex:
valueFwd = i+1
valueBwd = i+1
if useEdgeLength:
valueFwd = Edge.Length(edge)
valueBwd = Edge.Length(edge)
matrix[svi][evi] = valueFwd
if bidir:
matrix[evi][svi] = valueBwd
return matrix
@staticmethod
def AdjacencyList(graph, vertexKey=None, reverse=True, tolerance=0.0001):
"""
Returns the adjacency list of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_list.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexKey : str , optional
If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None.
reverse : bool , optional
If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The adjacency list.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
from topologicpy.Helper import Helper
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AdjacencyList - Error: The input graph is not a valid graph. Returning None.")
return None
vertices = Graph.Vertices(graph)
if not vertexKey == None:
sorting_values = []
for v in vertices:
d = Topology.Dictionary(v)
value = Dictionary.ValueAtKey(d, vertexKey)
sorting_values.append(value)
vertices = Helper.Sort(vertices, sorting_values)
if reverse == True:
vertices.reverse()
order = len(vertices)
adjList = []
for i in range(order):
tempRow = []
v = Graph.NearestVertex(graph, vertices[i])
adjVertices = Graph.AdjacentVertices(graph, v)
for adjVertex in adjVertices:
adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance)
if not adjIndex == None:
tempRow.append(adjIndex)
tempRow.sort()
adjList.append(tempRow)
return adjList
@staticmethod
def AddEdge(graph, edge, transferVertexDictionaries=False, transferEdgeDictionaries=False, tolerance=0.0001):
"""
Adds the input edge to the input Graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
edge : topologic_core.Edge
The input edge.
transferDictionaries : bool, optional
If set to True, the dictionaries of the edge and its vertices are transfered to the graph.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph with the input edge added to it.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
def addIfUnique(graph_vertices, vertex, tolerance):
unique = True
returnVertex = vertex
for gv in graph_vertices:
if (Vertex.Distance(vertex, gv) < tolerance):
if transferVertexDictionaries == True:
gd = Topology.Dictionary(gv)
vd = Topology.Dictionary(vertex)
gk = gd.Keys()
vk = vd.Keys()
d = None
if (len(gk) > 0) and (len(vk) > 0):
d = Dictionary.ByMergedDictionaries([gd, vd])
elif (len(gk) > 0) and (len(vk) < 1):
d = gd
elif (len(gk) < 1) and (len(vk) > 0):
d = vd
if d:
_ = Topology.SetDictionary(gv, d)
unique = False
returnVertex = gv
break
if unique:
graph_vertices.append(vertex)
return [graph_vertices, returnVertex]
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AddEdge - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(edge, "Edge"):
print("Graph.AddEdge - Error: The input edge is not a valid edge. Returning None.")
return None
graph_vertices = Graph.Vertices(graph)
graph_edges = Graph.Edges(graph, graph_vertices, tolerance)
vertices = Edge.Vertices(edge)
new_vertices = []
for vertex in vertices:
graph_vertices, nv = addIfUnique(graph_vertices, vertex, tolerance)
new_vertices.append(nv)
new_edge = Edge.ByVertices([new_vertices[0], new_vertices[1]], tolerance=tolerance)
if transferEdgeDictionaries == True:
d = Topology.Dictionary(edge)
keys = Dictionary.Keys(d)
if isinstance(keys, list):
if len(keys) > 0:
_ = Topology.SetDictionary(new_edge, d)
graph_edges.append(new_edge)
new_graph = Graph.ByVerticesEdges(graph_vertices, graph_edges)
return new_graph
@staticmethod
def AddVertex(graph, vertex, tolerance=0.0001):
"""
Adds the input vertex to the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph with the input vertex added to it.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AddVertex - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.AddVertex - Error: The input vertex is not a valid vertex. Returning None.")
return None
_ = graph.AddVertices([vertex], tolerance)
return graph
@staticmethod
def AddVertices(graph, vertices, tolerance=0.0001):
"""
Adds the input vertex to the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertices : list
The input list of vertices.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph with the input vertex added to it.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AddVertices - Error: The input graph is not a valid graph. Returning None.")
return None
if not isinstance(vertices, list):
print("Graph.AddVertices - Error: The input list of vertices is not a valid list. Returning None.")
return None
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
if len(vertices) < 1:
print("Graph.AddVertices - Error: Could not find any valid vertices in the input list of vertices. Returning None.")
return None
_ = graph.AddVertices(vertices, tolerance)
return graph
@staticmethod
def AdjacentVertices(graph, vertex):
"""
Returns the list of vertices connected to the input vertex.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
the input vertex.
Returns
-------
list
The list of adjacent vertices.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AdjacentVertices - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.AdjacentVertices - Error: The input vertex is not a valid vertex. Returning None.")
return None
vertices = []
_ = graph.AdjacentVertices(vertex, vertices)
return list(vertices)
@staticmethod
def AllPaths(graph, vertexA, vertexB, timeLimit=10):
"""
Returns all the paths that connect the input vertices within the allowed time limit in seconds.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
timeLimit : int , optional
The time limit in second. The default is 10 seconds.
Returns
-------
list
The list of all paths (wires) found within the time limit.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.AllPaths - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.AllPaths - Error: The input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.AllPaths - Error: The input vertexB is not a valid vertex. Returning None.")
return None
paths = []
_ = graph.AllPaths(vertexA, vertexB, True, timeLimit, paths)
return paths
@staticmethod
def AverageClusteringCoefficient(graph, mantissa=6):
"""
Returns the average clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient.
Parameters
----------
graph : topologic_core.Graph
The input graph.
mantissa : int , optional
The desired length of the mantissa. The default is 6.
Returns
-------
float
The average clustering coefficient of the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.")
return None
vertices = Graph.Vertices(graph)
if len(vertices) < 1:
print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is a NULL graph. Returning None.")
return None
if len(vertices) == 1:
return 0.0
lcc = Graph.LocalClusteringCoefficient(graph, vertices)
acc = round(sum(lcc)/float(len(lcc)), mantissa)
return acc
@staticmethod
def BOTGraph(graph,
bidirectional=False,
includeAttributes=False,
includeLabel=False,
includeGeometry=False,
siteLabel = "Site_0001",
siteDictionary = None,
buildingLabel = "Building_0001",
buildingDictionary = None ,
storeyPrefix = "Storey",
floorLevels =[],
labelKey="label",
typeKey="type",
geometryKey="brep",
spaceType = "space",
wallType = "wall",
slabType = "slab",
doorType = "door",
windowType = "window",
contentType = "content"
):
"""
Creates an RDF graph according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/.
Parameters
----------
graph : topologic_core.Graph
The input graph.
bidirectional : bool , optional
If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False.
includeAttributes : bool , optional
If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
includeLabel : bool , optional
If set to True, a label is attached to each node. Otherwise, it is not. The default is False.
includeGeometry : bool , optional
If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
siteLabel : str , optional
The desired site label. The default is "Site_0001".
siteDictionary : dict , optional
The dictionary of site attributes to include in the output. The default is None.
buildingLabel : str , optional
The desired building label. The default is "Building_0001".
buildingDictionary : dict , optional
The dictionary of building attributes to include in the output. The default is None.
storeyPrefix : str , optional
The desired prefixed to use for each building storey. The default is "Storey".
floorLevels : list , optional
The list of floor levels. This should be a numeric list, sorted from lowest to highest.
If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute.
labelKey : str , optional
The dictionary key to use to look up the label of the node. The default is "label".
typeKey : str , optional
The dictionary key to use to look up the type of the node. The default is "type".
geometryKey : str , optional
The dictionary key to use to look up the geometry of the node. The default is "brep".
spaceType : str , optional
The dictionary string value to use to look up vertices of type "space". The default is "space".
wallType : str , optional
The dictionary string value to use to look up vertices of type "wall". The default is "wall".
slabType : str , optional
The dictionary string value to use to look up vertices of type "slab". The default is "slab".
doorType : str , optional
The dictionary string value to use to look up vertices of type "door". The default is "door".
windowType : str , optional
The dictionary string value to use to look up vertices of type "window". The default is "window".
contentType : str , optional
The dictionary string value to use to look up vertices of type "content". The default is "contents".
Returns
-------
rdflib.graph.Graph
The rdf graph using the BOT ontology.
"""
from topologicpy.Helper import Helper
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
import os
import warnings
try:
from rdflib import Graph as RDFGraph
from rdflib import URIRef, Literal, Namespace
from rdflib.namespace import RDF, RDFS
except:
print("Graph.BOTGraph - Information: Installing required rdflib library.")
try:
os.system("pip install rdflib")
except:
os.system("pip install rdflib --user")
try:
from rdflib import Graph as RDFGraph
from rdflib import URIRef, Literal, Namespace
from rdflib.namespace import RDF, RDFS
print("Graph.BOTGraph - Information: rdflib library installed correctly.")
except:
warnings.warn("Graph.BOTGraph - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.")
return None
if floorLevels == None:
floorLevels = []
json_data = Graph.JSONData(graph, vertexLabelKey=labelKey)
# Create an empty RDF graph
bot_graph = RDFGraph()
# Define namespaces
rdf = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
bot = Namespace("https://w3id.org/bot#")
# Define a custom prefix mapping
bot_graph.namespace_manager.bind("bot", bot)
# Add site
site_uri = URIRef(siteLabel)
bot_graph.add((site_uri, rdf.type, bot.Site))
if includeLabel:
bot_graph.add((site_uri, RDFS.label, Literal(siteLabel)))
if Topology.IsInstance(siteDictionary, "Dictionary"):
keys = Dictionary.Keys(siteDictionary)
for key in keys:
value = Dictionary.ValueAtKey(siteDictionary, key)
if not (key == labelKey) and not (key == typeKey):
bot_graph.add((site_uri, bot[key], Literal(value)))
# Add building
building_uri = URIRef(buildingLabel)
bot_graph.add((building_uri, rdf.type, bot.Building))
if includeLabel:
bot_graph.add((building_uri, RDFS.label, Literal(buildingLabel)))
if Topology.IsInstance(buildingDictionary, "Dictionary"):
keys = Dictionary.Keys(buildingDictionary)
for key in keys:
value = Dictionary.ValueAtKey(buildingDictionary, key)
if key == labelKey:
if includeLabel:
bot_graph.add((building_uri, RDFS.label, Literal(value)))
elif key != typeKey:
bot_graph.add((building_uri, bot[key], Literal(value)))
# Add stories
# if floor levels are not given, then need to be computed
if len(floorLevels) == 0:
for node, attributes in json_data['vertices'].items():
if slabType.lower() in attributes[typeKey].lower():
floorLevels.append(attributes["z"])
floorLevels = list(set(floorLevels))
floorLevels.sort()
floorLevels = floorLevels[:-1]
storey_uris = []
n = max(len(str(len(floorLevels))),4)
for i, floor_level in enumerate(floorLevels):
storey_uri = URIRef(storeyPrefix+"_"+str(i+1).zfill(n))
bot_graph.add((storey_uri, rdf.type, bot.Storey))
if includeLabel:
bot_graph.add((storey_uri, RDFS.label, Literal(storeyPrefix+"_"+str(i+1).zfill(n))))
storey_uris.append(storey_uri)
# Add triples to relate building to site and stories to building
bot_graph.add((site_uri, bot.hasBuilding, building_uri))
if bidirectional:
bot_graph.add((building_uri, bot.isPartOf, site_uri)) # might not be needed
for storey_uri in storey_uris:
bot_graph.add((building_uri, bot.hasStorey, storey_uri))
if bidirectional:
bot_graph.add((storey_uri, bot.isPartOf, building_uri)) # might not be needed
# Add vertices as RDF resources
for node, attributes in json_data['vertices'].items():
node_uri = URIRef(node)
if spaceType.lower() in attributes[typeKey].lower():
bot_graph.add((node_uri, rdf.type, bot.Space))
# Find the storey it is on
z = attributes["z"]
level = Helper.Position(z, floorLevels)
if level > len(storey_uris):
level = len(storey_uris)
storey_uri = storey_uris[level-1]
bot_graph.add((storey_uri, bot.hasSpace, node_uri))
if bidirectional:
bot_graph.add((node_uri, bot.isPartOf, storey_uri)) # might not be needed
elif windowType.lower() in attributes[typeKey].lower():
bot_graph.add((node_uri, rdf.type, bot.Window))
elif doorType.lower() in attributes[typeKey].lower():
bot_graph.add((node_uri, rdf.type, bot.Door))
elif wallType.lower() in attributes[typeKey].lower():
bot_graph.add((node_uri, rdf.type, bot.Wall))
elif slabType.lower() in attributes[typeKey].lower():
bot_graph.add((node_uri, rdf.type, bot.Slab))
else:
bot_graph.add((node_uri, rdf.type, bot.Element))
if includeAttributes:
for key, value in attributes.items():
if key == geometryKey:
if includeGeometry:
bot_graph.add((node_uri, bot.hasSimpleGeometry, Literal(value)))
if key == labelKey:
if includeLabel:
bot_graph.add((node_uri, RDFS.label, Literal(value)))
elif key != typeKey and key != geometryKey:
bot_graph.add((node_uri, bot[key], Literal(value)))
if includeLabel:
for key, value in attributes.items():
if key == labelKey:
bot_graph.add((node_uri, RDFS.label, Literal(value)))
# Add edges as RDF triples
for edge, attributes in json_data['edges'].items():
source = attributes["source"]
target = attributes["target"]
source_uri = URIRef(source)
target_uri = URIRef(target)
if spaceType.lower() in json_data['vertices'][source][typeKey].lower() and spaceType.lower() in json_data['vertices'][target][typeKey].lower():
bot_graph.add((source_uri, bot.adjacentTo, target_uri))
if bidirectional:
bot_graph.add((target_uri, bot.adjacentTo, source_uri))
elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and wallType.lower() in json_data['vertices'][target][typeKey].lower():
bot_graph.add((target_uri, bot.interfaceOf, source_uri))
elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and slabType.lower() in json_data['vertices'][target][typeKey].lower():
bot_graph.add((target_uri, bot.interfaceOf, source_uri))
elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and contentType.lower() in json_data['vertices'][target][typeKey].lower():
bot_graph.add((source_uri, bot.containsElement, target_uri))
if bidirectional:
bot_graph.add((target_uri, bot.isPartOf, source_uri))
else:
bot_graph.add((source_uri, bot.connectsTo, target_uri))
if bidirectional:
bot_graph.add((target_uri, bot.connectsTo, source_uri))
return bot_graph
@staticmethod
def BOTString(graph,
format="turtle",
bidirectional=False,
includeAttributes=False,
includeLabel=False,
includeGeometry=False,
siteLabel = "Site_0001",
siteDictionary = None,
buildingLabel = "Building_0001",
buildingDictionary = None ,
storeyPrefix = "Storey",
floorLevels =[],
labelKey="label",
typeKey="type",
geometryKey="brep",
spaceType = "space",
wallType = "wall",
slabType = "slab",
doorType = "door",
windowType = "window",
contentType = "content",
):
"""
Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/.
Parameters
----------
graph : topologic_core.Graph
The input graph.
format : str , optional
The desired output format, the options are listed below. Thde default is "turtle".
turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks
xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0
json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants
ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded
n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things
trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs
trix : Trix , RDF/XML-like format for RDF quads
nquads : N-Quads , N-Triples-like format for RDF quads
bidirectional : bool , optional
If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False.
includeAttributes : bool , optional
If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
includeLabel : bool , optional
If set to True, a label is attached to each node. Otherwise, it is not. The default is False.
includeGeometry : bool , optional
If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
siteLabel : str , optional
The desired site label. The default is "Site_0001".
siteDictionary : dict , optional
The dictionary of site attributes to include in the output. The default is None.
buildingLabel : str , optional
The desired building label. The default is "Building_0001".
buildingDictionary : dict , optional
The dictionary of building attributes to include in the output. The default is None.
storeyPrefix : str , optional
The desired prefixed to use for each building storey. The default is "Storey".
floorLevels : list , optional
The list of floor levels. This should be a numeric list, sorted from lowest to highest.
If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute.
typeKey : str , optional
The dictionary key to use to look up the type of the node. The default is "type".
labelKey : str , optional
The dictionary key to use to look up the label of the node. The default is "label".
spaceType : str , optional
The dictionary string value to use to look up vertices of type "space". The default is "space".
wallType : str , optional
The dictionary string value to use to look up vertices of type "wall". The default is "wall".
slabType : str , optional
The dictionary string value to use to look up vertices of type "slab". The default is "slab".
doorType : str , optional
The dictionary string value to use to look up vertices of type "door". The default is "door".
windowType : str , optional
The dictionary string value to use to look up vertices of type "window". The default is "window".
contentType : str , optional
The dictionary string value to use to look up vertices of type "content". The default is "contents".
Returns
-------
str
The rdf graph serialized string using the BOT ontology.
"""
bot_graph = Graph.BOTGraph(graph,
bidirectional=bidirectional,
includeAttributes=includeAttributes,
includeLabel=includeLabel,
includeGeometry=includeGeometry,
siteLabel=siteLabel,
siteDictionary=siteDictionary,
buildingLabel=buildingLabel,
buildingDictionary=buildingDictionary,
storeyPrefix=storeyPrefix,
floorLevels=floorLevels,
labelKey=labelKey,
typeKey=typeKey,
geometryKey=geometryKey,
spaceType = spaceType,
wallType = wallType,
slabType = slabType,
doorType = doorType,
windowType = windowType,
contentType = contentType
)
return bot_graph.serialize(format=format)
@staticmethod
def BetweenessCentrality(graph, vertices=None, sources=None, destinations=None, tolerance=0.001):
"""
Returns the betweeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the betweeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Betweenness_centrality.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertices : list , optional
The input list of vertices. The default is None which means all vertices in the input graph are considered.
sources : list , optional
The input list of source vertices. The default is None which means all vertices in the input graph are considered.
destinations : list , optional
The input list of destination vertices. The default is None which means all vertices in the input graph are considered.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The betweeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Topology import Topology
def betweeness(vertices, topologies, tolerance=0.001):
returnList = [0] * len(vertices)
for topology in topologies:
t_vertices = Topology.Vertices(topology)
for t_v in t_vertices:
index = Vertex.Index(t_v, vertices, strict=False, tolerance=tolerance)
if not index == None:
returnList[index] = returnList[index]+1
return returnList
if not Topology.IsInstance(graph, "Graph"):
print("Graph.BetweenessCentrality - Error: The input graph is not a valid graph. Returning None.")
return None
graphVertices = Graph.Vertices(graph)
if not isinstance(vertices, list):
vertices = graphVertices
else:
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
if len(vertices) < 1:
print("Graph.BetweenessCentrality - Error: The input list of vertices does not contain valid vertices. Returning None.")
return None
if not isinstance(sources, list):
sources = graphVertices
else:
sources = [v for v in sources if Topology.IsInstance(v, "Vertex")]
if len(sources) < 1:
print("Graph.BetweenessCentrality - Error: The input list of sources does not contain valid vertices. Returning None.")
return None
if not isinstance(destinations, list):
destinations = graphVertices
else:
destinations = [v for v in destinations if Topology.IsInstance(v, "Vertex")]
if len(destinations) < 1:
print("Graph.BetweenessCentrality - Error: The input list of destinations does not contain valid vertices. Returning None.")
return None
paths = []
try:
for so in tqdm(sources, desc="Computing Shortest Paths", leave=False):
v1 = Graph.NearestVertex(graph, so)
for si in destinations:
v2 = Graph.NearestVertex(graph, si)
if not v1 == v2:
path = Graph.ShortestPath(graph, v1, v2)
if path:
paths.append(path)
except:
for so in sources:
v1 = Graph.NearestVertex(graph, so)
for si in destinations:
v2 = Graph.NearestVertex(graph, si)
if not v1 == v2:
path = Graph.ShortestPath(graph, v1, v2)
if path:
paths.append(path)
values = betweeness(vertices, paths, tolerance=tolerance)
minValue = min(values)
maxValue = max(values)
size = maxValue - minValue
values = [(v-minValue)/size for v in values]
return values
@staticmethod
def ByAdjacencyMatrixCSVPath(path):
"""
Returns graphs according to the input path. This method assumes the CSV files follow an adjacency matrix schema.
Parameters
----------
path : str
The file path to the adjacency matrix CSV file.
Returns
-------
topologic_core.Graph
The created graph.
"""
# Read the adjacency matrix from CSV file using pandas
adjacency_matrix_df = pd.read_csv(path, header=None)
# Convert DataFrame to a nested list
adjacency_matrix = adjacency_matrix_df.values.tolist()
return Graph.ByAdjacencyMatrix(adjacencyMatrix=adjacency_matrix)
@staticmethod
def ByAdjacencyMatrix(adjacencyMatrix, xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5):
"""
Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema.
Parameters
----------
adjacencyMatrix : list
The adjacency matrix expressed as a nested list of 0s and 1s.
xMin : float, optional
The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
yMin : float, optional
The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
zMin : float, optional
The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
xMax : float, optional
The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
yMax : float, optional
The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
zMax : float, optional
The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
Returns
-------
topologic_core.Graph
The created graph.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
import random
if not isinstance(adjacencyMatrix, list):
print("Graph.ByAdjacencyMatrix - Error: The input adjacencyMatrix parameter is not a valid list. Returning None.")
return None
# Add vertices with random coordinates
vertices = []
for i in range(len(adjacencyMatrix)):
x, y, z = random.uniform(xMin,xMax), random.uniform(yMin,yMax), random.uniform(zMin,zMax)
vertices.append(Vertex.ByCoordinates(x, y, z))
# Create the graph using vertices and edges
if len(vertices) == 0:
print("Graph.ByAdjacencyMatrix - Error: The graph does not contain any vertices. Returning None.")
return None
# Add edges based on the adjacency matrix
edges = []
for i in range(len(adjacencyMatrix)):
for j in range(i+1, len(adjacencyMatrix)):
if not adjacencyMatrix[i][j] == 0:
edges.append(Edge.ByVertices([vertices[i], vertices[j]]))
return Graph.ByVerticesEdges(vertices, edges)
@staticmethod
def ByBOTGraph(botGraph,
includeContext = False,
xMin = -0.5,
xMax = 0.5,
yMin = -0.5,
yMax = 0.5,
zMin = -0.5,
zMax = 0.5,
tolerance = 0.0001
):
def value_by_string(s):
if s.lower() == "true":
return True
if s.lower() == "false":
return False
vt = "str"
s2 = s.strip("-")
if s2.isnumeric():
vt = "int"
else:
try:
s3 = s2.split(".")[0]
s4 = s2.split(".")[1]
if (s3.isnumeric() or s4.isnumeric()):
vt = "float"
except:
vt = "str"
if vt == "str":
return s
elif vt == "int":
return int(s)
elif vt == "float":
return float(s)
def collect_nodes_by_type(rdf_graph, node_type=None):
results = set()
if node_type is not None:
for subj, pred, obj in rdf_graph.triples((None, None, None)):
if "type" in pred.lower():
if node_type.lower() in obj.lower():
results.add(subj)
return list(results)
def collect_attributes_for_subject(rdf_graph, subject):
attributes = {}
for subj, pred, obj in rdf_graph.triples((subject, None, None)):
predicate_str = str(pred)
object_str = str(obj)
attributes[predicate_str] = object_str
return attributes
def get_triples_by_predicate_type(rdf_graph, predicate_type):
triples = []
for subj, pred, obj in rdf_graph:
if pred.split('#')[-1].lower() == predicate_type.lower():
triples.append((str(subj), str(pred), str(obj)))
return triples
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Graph import Graph
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
import random
try:
import rdflib
except:
print("Graph.BOTGraph - Information: Installing required rdflib library.")
try:
os.system("pip install rdflib")
except:
os.system("pip install rdflib --user")
try:
import rdflib
print("Graph.BOTGraph - Information: rdflib library installed correctly.")
except:
warnings.warn("Graph.BOTGraph - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.")
return None
predicates = ['adjacentto', 'interfaceof', 'containselement', 'connectsto']
bot_types = ['Space', 'Wall', 'Slab', 'Door', 'Window', 'Element']
if includeContext:
predicates += ['hasspace', 'hasbuilding', 'hasstorey']
bot_types += ['Site', 'Building', 'Storey']
namespaces = botGraph.namespaces()
for ns in namespaces:
if 'bot' in ns[0].lower():
bot_namespace = ns
break
ref = bot_namespace[1]
nodes = []
for bot_type in bot_types:
node_type = rdflib.term.URIRef(ref+bot_type)
nodes +=collect_nodes_by_type(botGraph, node_type=node_type)
vertices = []
dic = {}
for node in nodes:
x, y, z = random.uniform(xMin,xMax), random.uniform(yMin,yMax), random.uniform(zMin,zMax)
d_keys = ["bot_id"]
d_values = [str(node)]
attributes = collect_attributes_for_subject(botGraph, node)
keys = attributes.keys()
for key in keys:
key_type = key.split('#')[-1]
if key_type.lower() not in predicates:
if 'x' == key_type.lower():
x = value_by_string(attributes[key])
d_keys.append('x')
d_values.append(x)
elif 'y' == key_type.lower():
y = value_by_string(attributes[key])
d_keys.append('y')
d_values.append(y)
elif 'z' == key_type.lower():
z = value_by_string(attributes[key])
d_keys.append('z')
d_values.append(z)
else:
d_keys.append(key_type.lower())
d_values.append(value_by_string(attributes[key].split("#")[-1]))
d = Dictionary.ByKeysValues(d_keys, d_values)
v = Vertex.ByCoordinates(x,y,z)
v = Topology.SetDictionary(v, d)
dic[str(node)] = v
vertices.append(v)
edges = []
for predicate in predicates:
triples = get_triples_by_predicate_type(botGraph, predicate)
for triple in triples:
subj = triple[0]
obj = triple[2]
sv = dic[subj]
ev = dic[obj]
e = Edge.ByVertices([sv,ev], tolerance=tolerance)
d = Dictionary.ByKeyValue("type", predicate)
e = Topology.SetDictionary(e, d)
edges.append(e)
return Graph.ByVerticesEdges(vertices, edges)
@staticmethod
def ByBOTPath(path,
includeContext = False,
xMin = -0.5,
xMax = 0.5,
yMin = -0.5,
yMax = 0.5,
zMin = -0.5,
zMax = 0.5,
tolerance = 0.0001
):
try:
from rdflib import Graph as RDFGraph
except:
print("Graph.ByBOTPath - Information: Installing required rdflib library.")
try:
os.system("pip install rdflib")
except:
os.system("pip install rdflib --user")
try:
from rdflib import Graph as RDFGraph
print("Graph.ByBOTPath - Information: rdflib library installed correctly.")
except:
warnings.warn("Graph.ByBOTPath - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.")
return None
bot_graph = RDFGraph()
bot_graph.parse(path)
return Graph.ByBOTGraph(bot_graph,
includeContext = includeContext,
xMin = xMin,
xMax = xMax,
yMin = yMin,
yMax = yMax,
zMin = zMin,
zMax = zMax,
tolerance = tolerance
)
@staticmethod
def ByCSVPath(path,
graphIDHeader="graph_id", graphLabelHeader="label", graphFeaturesHeader="feat", graphFeaturesKeys=[],
edgeSRCHeader="src_id", edgeDSTHeader="dst_id", edgeLabelHeader="label", edgeTrainMaskHeader="train_mask",
edgeValidateMaskHeader="val_mask", edgeTestMaskHeader="test_mask", edgeFeaturesHeader="feat", edgeFeaturesKeys=[],
nodeIDHeader="node_id", nodeLabelHeader="label", nodeTrainMaskHeader="train_mask",
nodeValidateMaskHeader="val_mask", nodeTestMaskHeader="test_mask", nodeFeaturesHeader="feat", nodeXHeader="X", nodeYHeader="Y", nodeZHeader="Z",
nodeFeaturesKeys=[], tolerance=0.0001):
"""
Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema.
Parameters
----------
path : str
The path to the folder containing the .yaml and .csv files for graphs, edges, and nodes.
graphIDHeader : str , optional
The column header string used to specify the graph id. The default is "graph_id".
graphLabelHeader : str , optional
The column header string used to specify the graph label. The default is "label".
graphFeaturesHeader : str , optional
The column header string used to specify the graph features. The default is "feat".
edgeSRCHeader : str , optional
The column header string used to specify the source vertex id of edges. The default is "src_id".
edgeDSTHeader : str , optional
The column header string used to specify the destination vertex id of edges. The default is "dst_id".
edgeLabelHeader : str , optional
The column header string used to specify the label of edges. The default is "label".
edgeTrainMaskHeader : str , optional
The column header string used to specify the train mask of edges. The default is "train_mask".
edgeValidateMaskHeader : str , optional
The column header string used to specify the validate mask of edges. The default is "val_mask".
edgeTestMaskHeader : str , optional
The column header string used to specify the test mask of edges. The default is "test_mask".
edgeFeaturesHeader : str , optional
The column header string used to specify the features of edges. The default is "feat".
edgeFeaturesKeys : list , optional
The list of dicitonary keys to use to index the edge features. The length of this list must match the length of edge features. The default is [].
nodeIDHeader : str , optional
The column header string used to specify the id of nodes. The default is "node_id".
nodeLabelHeader : str , optional
The column header string used to specify the label of nodes. The default is "label".
nodeTrainMaskHeader : str , optional
The column header string used to specify the train mask of nodes. The default is "train_mask".
nodeValidateMaskHeader : str , optional
The column header string used to specify the validate mask of nodes. The default is "val_mask".
nodeTestMaskHeader : str , optional
The column header string used to specify the test mask of nodes. The default is "test_mask".
nodeFeaturesHeader : str , optional
The column header string used to specify the features of nodes. The default is "feat".
nodeXHeader : str , optional
The column header string used to specify the X coordinate of nodes. The default is "X".
nodeYHeader : str , optional
The column header string used to specify the Y coordinate of nodes. The default is "Y".
nodeZHeader : str , optional
The column header string used to specify the Z coordinate of nodes. The default is "Z".
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
dict
The dictionary of DGL graphs and labels found in the input CSV files. The keys in the dictionary are "graphs", "labels", "features"
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
import os
from os.path import exists, isdir
import yaml
import glob
import random
import numbers
def find_yaml_files(folder_path):
yaml_files = glob.glob(f"{folder_path}/*.yaml")
return yaml_files
def read_yaml(file_path):
with open(file_path, 'r') as file:
data = yaml.safe_load(file)
edge_data = data.get('edge_data', [])
node_data = data.get('node_data', [])
graph_data = data.get('graph_data', {})
edges_path = edge_data[0].get('file_name') if edge_data else None
nodes_path = node_data[0].get('file_name') if node_data else None
graphs_path = graph_data.get('file_name')
return graphs_path, edges_path, nodes_path
if not exists(path):
print("Graph.ByCSVPath - Error: the input path parameter does not exists. Returning None.")
return None
if not isdir(path):
print("Graph.ByCSVPath - Error: the input path parameter is not a folder. Returning None.")
return None
yaml_files = find_yaml_files(path)
if len(yaml_files) < 1:
print("Graph.ByCSVPath - Error: the input path parameter does not contain any valid YAML files. Returning None.")
return None
yaml_file = yaml_files[0]
yaml_file_path = os.path.join(path, yaml_file)
graphs_path, edges_path, nodes_path = read_yaml(yaml_file_path)
if not graphs_path == None:
graphs_path = os.path.join(path, graphs_path)
if graphs_path == None:
print("Graph.ByCSVPath - Warning: a graphs.csv file does not exist inside the folder specified by the input path parameter. Will assume the dataset includes only one graph.")
graphs_df = pd.DataFrame()
graph_ids=[0]
graph_labels=[0]
graph_features=[None]
else:
graphs_df = pd.read_csv(graphs_path)
graph_ids = []
graph_labels = []
graph_features = []
if not edges_path == None:
edges_path = os.path.join(path, edges_path)
if not exists(edges_path):
print("Graph.ByCSVPath - Error: an edges.csv file does not exist inside the folder specified by the input path parameter. Returning None.")
return None
edges_path = os.path.join(path, edges_path)
edges_df = pd.read_csv(edges_path)
grouped_edges = edges_df.groupby(graphIDHeader)
if not nodes_path == None:
nodes_path = os.path.join(path, nodes_path)
if not exists(nodes_path):
print("Graph.ByCSVPath - Error: a nodes.csv file does not exist inside the folder specified by the input path parameter. Returning None.")
return None
nodes_df = pd.read_csv(nodes_path)
# Group nodes and nodes by their 'graph_id'
grouped_nodes = nodes_df.groupby(graphIDHeader)
if len(nodeFeaturesKeys) == 0:
node_keys = [nodeIDHeader, nodeLabelHeader, "mask", nodeFeaturesHeader]
else:
node_keys = [nodeIDHeader, nodeLabelHeader, "mask"]+nodeFeaturesKeys
if len(edgeFeaturesKeys) == 0:
edge_keys = [edgeLabelHeader, "mask", edgeFeaturesHeader]
else:
edge_keys = [edgeLabelHeader, "mask"]+edgeFeaturesKeys
if len(graphFeaturesKeys) == 0:
graph_keys = [graphIDHeader, graphLabelHeader, graphFeaturesHeader]
else:
graph_keys = [graphIDHeader, graphLabelHeader]+graphFeaturesKeys
# Iterate through the graphs DataFrame
for index, row in graphs_df.iterrows():
graph_ids.append(row[graphIDHeader])
graph_labels.append(row[graphLabelHeader])
graph_features.append(row[graphFeaturesHeader])
vertices_ds = [] # A list to hold the vertices data structures until we can build the actual graphs
# Iterate through the grouped nodes DataFrames
for graph_id, group_node_df in grouped_nodes:
vertices = []
verts = [] #This is a list of x, y, z tuples to make sure the vertices have unique locations.
n_verts = 0
for index, row in group_node_df.iterrows():
n_verts += 1
node_id = row[nodeIDHeader]
label = row[nodeLabelHeader]
train_mask = row[nodeTrainMaskHeader]
val_mask = row[nodeValidateMaskHeader]
test_mask = row[nodeTestMaskHeader]
mask = 0
if [train_mask, val_mask, test_mask] == [True, False, False]:
mask = 0
elif [train_mask, val_mask, test_mask] == [False, True, False]:
mask = 1
elif [train_mask, val_mask, test_mask] == [False, False, True]:
mask = 2
else:
mask = 0
features = row[nodeFeaturesHeader]
x = row[nodeXHeader]
y = row[nodeYHeader]
z = row[nodeZHeader]
if not isinstance(x, numbers.Number):
x = random.randrange(0,1000)
if not isinstance(y, numbers.Number):
y = random.randrange(0,1000)
if not isinstance(z, numbers.Number):
z = random.randrange(0,1000)
while [x, y, z] in verts:
x = x + random.randrange(10000,30000,1000)
y = y + random.randrange(4000,6000, 100)
z = z + random.randrange(70000,90000, 1000)
verts.append([x, y, z])
v = Vertex.ByCoordinates(x, y, z)
if Topology.IsInstance(v, "Vertex"):
if len(nodeFeaturesKeys) == 0:
values = [node_id, label, mask, features]
else:
values = [node_id, label, mask]
featureList = features.split(",")
featureList = [float(s) for s in featureList]
values = [node_id, label, mask]+featureList
d = Dictionary.ByKeysValues(node_keys, values)
if Topology.IsInstance(d, "Dictionary"):
v = Topology.SetDictionary(v, d)
else:
print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created vertex.")
vertices.append(v)
else:
print("Graph.ByCSVPath - Warning: Failed to create and add a vertex to the list of vertices.")
vertices_ds.append(vertices)
edges_ds = [] # A list to hold the vertices data structures until we can build the actual graphs
# Access specific columns within the grouped DataFrame
for graph_id, group_edge_df in grouped_edges:
#vertices = vertices_ds[graph_id]
edges = []
es = [] # a list to check for duplicate edges
duplicate_edges = 0
for index, row in group_edge_df.iterrows():
src_id = int(row[edgeSRCHeader])
dst_id = int(row[edgeDSTHeader])
label = row[nodeLabelHeader]
train_mask = row[edgeTrainMaskHeader]
val_mask = row[edgeValidateMaskHeader]
test_mask = row[edgeTestMaskHeader]
mask = 0
if [train_mask, val_mask, test_mask] == [True, False, False]:
mask = 0
elif [train_mask, val_mask, test_mask] == [False, True, False]:
mask = 1
elif [train_mask, val_mask, test_mask] == [False, False, True]:
mask = 2
else:
mask = 0
features = row[edgeFeaturesHeader]
if len(edgeFeaturesKeys) == 0:
values = [label, mask, features]
else:
featureList = features.split(",")
featureList = [float(s) for s in featureList]
values = [label, mask]+featureList
if not (src_id == dst_id) and not [src_id, dst_id] in es and not [dst_id, src_id] in es:
es.append([src_id, dst_id])
edge = Edge.ByVertices([vertices[src_id], vertices[dst_id]], tolerance=tolerance)
if Topology.IsInstance(edge, "Edge"):
d = Dictionary.ByKeysValues(edge_keys, values)
if Topology.IsInstance(d, "Dictionary"):
edge = Topology.SetDictionary(edge, d)
else:
print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created edge.")
edges.append(edge)
else:
print("Graph.ByCSVPath - Warning: Failed to create and add an edge to the list of edges.")
else:
duplicate_edges += 1
if duplicate_edges > 0:
print("Graph.ByCSVPath - Warning: Found", duplicate_edges, "duplicate edges in graph id:", graph_id)
edges_ds.append(edges)
# Build the graphs
graphs = []
for i, vertices, in enumerate(vertices_ds):
edges = edges_ds[i]
g = Graph.ByVerticesEdges(vertices, edges)
temp_v = Graph.Vertices(g)
temp_e = Graph.Edges(g)
if Topology.IsInstance(g, "Graph"):
if len(graphFeaturesKeys) == 0:
values = [graph_ids[i], graph_labels[i], graph_features[i]]
else:
values = [graph_ids[i], graph_labels[i]]
featureList = graph_features[i].split(",")
featureList = [float(s) for s in featureList]
values = [graph_ids[i], graph_labels[i]]+featureList
l1 = len(graph_keys)
l2 = len(values)
if not l1 == l2:
print("Graph.ByCSVPath - Error: The length of the keys and values lists do not match. Returning None.")
return None
d = Dictionary.ByKeysValues(graph_keys, values)
if Topology.IsInstance(d, "Dictionary"):
g = Graph.SetDictionary(g, d)
else:
print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created graph.")
graphs.append(g)
else:
print("Graph.ByCSVPath - Error: Failed to create and add a graph to the list of graphs.")
return {"graphs": graphs, "labels": graph_labels, "features": graph_features}
@staticmethod
def ByDGCNNFile(file, key: str = "label", tolerance: float = 0.0001):
"""
Creates a graph from a DGCNN File.
Parameters
----------
file : file object
The input file.
key : str , optional
The desired key for storing the node label. The default is "label".
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
dict
A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'.
"""
if not file:
print("Graph.ByDGCNNFile - Error: The input file is not a valid file. Returning None.")
return None
dgcnn_string = file.read()
file.close()
return Graph.ByDGCNNString(dgcnn_string, key=key, tolerance=tolerance)
@staticmethod
def ByDGCNNPath(path, key: str = "label", tolerance: float = 0.0001):
"""
Creates a graph from a DGCNN path.
Parameters
----------
path : str
The input file path.
key : str , optional
The desired key for storing the node label. The default is "label".
tolerance : str , optional
The desired tolerance. The default is 0.0001.
Returns
-------
dict
A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'.
"""
if not path:
print("Graph.ByDGCNNPath - Error: the input path is not a valid path. Returning None.")
return None
try:
file = open(path)
except:
print("Graph.ByDGCNNPath - Error: the DGCNN file is not a valid file. Returning None.")
return None
return Graph.ByDGCNNFile(file, key=key, tolerance=tolerance)
@staticmethod
def ByDGCNNString(string, key: str ="label", tolerance: float = 0.0001):
"""
Creates a graph from a DGCNN string.
Parameters
----------
string : str
The input string.
key : str , optional
The desired key for storing the node label. The default is "label".
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
dict
A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
import random
def verticesByCoordinates(x_coords, y_coords):
vertices = []
for i in range(len(x_coords)):
vertices.append(Vertex.ByCoordinates(x_coords[i], y_coords[i], 0))
return vertices
graphs = []
labels = []
lines = string.split("\n")
n_graphs = int(lines[0])
index = 1
for i in range(n_graphs):
edges = []
line = lines[index].split()
n_nodes = int(line[0])
graph_label = int(line[1])
labels.append(graph_label)
index+=1
x_coordinates = random.sample(range(0, n_nodes), n_nodes)
y_coordinates = random.sample(range(0, n_nodes), n_nodes)
vertices = verticesByCoordinates(x_coordinates, y_coordinates)
for j in range(n_nodes):
line = lines[index+j].split()
node_label = int(line[0])
node_dict = Dictionary.ByKeysValues([key], [node_label])
Topology.SetDictionary(vertices[j], node_dict)
for j in range(n_nodes):
line = lines[index+j].split()
sv = vertices[j]
adj_vertices = line[2:]
for adj_vertex in adj_vertices:
ev = vertices[int(adj_vertex)]
e = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance)
edges.append(e)
index+=n_nodes
graphs.append(Graph.ByVerticesEdges(vertices, edges))
return {'graphs':graphs, 'labels':labels}
@staticmethod
def ByIFCFile(file, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5):
"""
Create a Graph from an IFC file. This code is partially based on code from Bruno Postle.
Parameters
----------
file : file
The input IFC file
includeTypes : list , optional
A list of IFC object types to include in the graph. The default is [] which means all object types are included.
excludeTypes : list , optional
A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded.
includeRels : list , optional
A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included.
excludeRels : list , optional
A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded.
xMin : float, optional
The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
yMin : float, optional
The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
zMin : float, optional
The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
xMax : float, optional
The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
yMax : float, optional
The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
zMax : float, optional
The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
Returns
-------
topologic_core.Graph
The created graph.
"""
from topologicpy.Topology import Topology
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Graph import Graph
from topologicpy.Dictionary import Dictionary
try:
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.util.element
import ifcopenshell.util.shape
import ifcopenshell.geom
except:
print("Graph.ByIFCFile - Warning: Installing required ifcopenshell library.")
try:
os.system("pip install ifcopenshell")
except:
os.system("pip install ifcopenshell --user")
try:
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.util.element
import ifcopenshell.util.shape
import ifcopenshell.geom
print("Graph.ByIFCFile - Warning: ifcopenshell library installed correctly.")
except:
warnings.warn("Graph.ByIFCFile - Error: Could not import ifcopenshell. Please try to install ifcopenshell manually. Returning None.")
return None
import random
def vertexAtKeyValue(vertices, key, value):
for v in vertices:
d = Topology.Dictionary(v)
d_value = Dictionary.ValueAtKey(d, key)
if value == d_value:
return v
return None
def IFCObjects(ifc_file, include=[], exclude=[]):
include = [s.lower() for s in include]
exclude = [s.lower() for s in exclude]
all_objects = ifc_file.by_type('IfcProduct')
return_objects = []
for obj in all_objects:
is_a = obj.is_a().lower()
if is_a in exclude:
continue
if is_a in include or len(include) == 0:
return_objects.append(obj)
return return_objects
def IFCObjectTypes(ifc_file):
products = IFCObjects(ifc_file)
obj_types = []
for product in products:
obj_types.append(product.is_a())
obj_types = list(set(obj_types))
obj_types.sort()
return obj_types
def IFCRelationshipTypes(ifc_file):
rel_types = [ifc_rel.is_a() for ifc_rel in ifc_file.by_type("IfcRelationship")]
rel_types = list(set(rel_types))
rel_types.sort()
return rel_types
def IFCRelationships(ifc_file, include=[], exclude=[]):
include = [s.lower() for s in include]
exclude = [s.lower() for s in exclude]
rel_types = [ifc_rel.is_a() for ifc_rel in ifc_file.by_type("IfcRelationship")]
rel_types = list(set(rel_types))
relationships = []
for ifc_rel in ifc_file.by_type("IfcRelationship"):
rel_type = ifc_rel.is_a().lower()
if rel_type in exclude:
continue
if rel_type in include or len(include) == 0:
relationships.append(ifc_rel)
return relationships
def vertexByIFCObject(ifc_object, object_types, restrict=False):
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_BREP_DATA,False)
settings.set(settings.SEW_SHELLS,True)
settings.set(settings.USE_WORLD_COORDS,True)
try:
shape = ifcopenshell.geom.create_shape(settings, ifc_object)
except:
shape = None
if shape or restrict == False: #Only add vertices of entities that have 3D geometries.
obj_id = ifc_object.id()
psets = ifcopenshell.util.element.get_psets(ifc_object)
obj_type = ifc_object.is_a()
obj_type_id = object_types.index(obj_type)
name = "Untitled"
LongName = "Untitled"
try:
name = ifc_object.Name
except:
name = "Untitled"
try:
LongName = ifc_object.LongName
except:
LongName = name
if name == None:
name = "Untitled"
if LongName == None:
LongName = "Untitled"
label = str(obj_id)+" "+LongName+" ("+obj_type+" "+str(obj_type_id)+")"
try:
grouped_verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
vertices = [Vertex.ByCoordinates(list(coords)) for coords in grouped_verts]
centroid = Vertex.Centroid(vertices)
except:
x = random.uniform(xMin,xMax)
y = random.uniform(yMin,yMax)
z = random.uniform(zMin,zMax)
centroid = Vertex.ByCoordinates(x, y, z)
d = Dictionary.ByKeysValues(["id","psets", "type", "type_id", "name", "label"], [obj_id, psets, obj_type, obj_type_id, name, label])
centroid = Topology.SetDictionary(centroid, d)
return centroid
return None
def edgesByIFCRelationships(ifc_relationships, ifc_types, vertices):
tuples = []
edges = []
for ifc_rel in ifc_relationships:
source = None
destinations = []
if ifc_rel.is_a("IfcRelAggregates"):
source = ifc_rel.RelatingObject
destinations = ifc_rel.RelatedObjects
if ifc_rel.is_a("IfcRelNests"):
source = ifc_rel.RelatingObject
destinations = ifc_rel.RelatedObjects
if ifc_rel.is_a("IfcRelAssignsToGroup"):
source = ifc_rel.RelatingGroup
destinations = ifc_rel.RelatedObjects
if ifc_rel.is_a("IfcRelConnectsPathElements"):
source = ifc_rel.RelatingElement
destinations = [ifc_rel.RelatedElement]
if ifc_rel.is_a("IfcRelConnectsStructuralMember"):
source = ifc_rel.RelatingStructuralMember
destinations = [ifc_rel.RelatedStructuralConnection]
if ifc_rel.is_a("IfcRelContainedInSpatialStructure"):
source = ifc_rel.RelatingStructure
destinations = ifc_rel.RelatedElements
if ifc_rel.is_a("IfcRelFillsElement"):
source = ifc_rel.RelatingOpeningElement
destinations = [ifc_rel.RelatedBuildingElement]
if ifc_rel.is_a("IfcRelSpaceBoundary"):
source = ifc_rel.RelatingSpace
destinations = [ifc_rel.RelatedBuildingElement]
if ifc_rel.is_a("IfcRelVoidsElement"):
source = ifc_rel.RelatingBuildingElement
destinations = [ifc_rel.RelatedOpeningElement]
if source:
sv = vertexAtKeyValue(vertices, key="id", value=source.id())
if sv:
si = Vertex.Index(sv, vertices)
for destination in destinations:
if destination == None:
continue
ev = vertexAtKeyValue(vertices, key="id", value=destination.id())
if ev:
ei = Vertex.Index(ev, vertices)
if not([si,ei] in tuples or [ei,si] in tuples):
tuples.append([si,ei])
e = Edge.ByVertices([sv,ev])
d = Dictionary.ByKeysValues(["id", "name", "type"], [ifc_rel.id(), ifc_rel.Name, ifc_rel.is_a()])
e = Topology.SetDictionary(e, d)
edges.append(e)
return edges
ifc_types = IFCObjectTypes(file)
ifc_objects = IFCObjects(file, include=includeTypes, exclude=excludeTypes)
vertices = []
for ifc_object in ifc_objects:
v = vertexByIFCObject(ifc_object, ifc_types)
if v:
vertices.append(v)
if len(vertices) > 0:
ifc_relationships = IFCRelationships(file, include=includeRels, exclude=excludeRels)
edges = edgesByIFCRelationships(ifc_relationships, ifc_types, vertices)
g = Graph.ByVerticesEdges(vertices, edges)
else:
g = None
return g
@staticmethod
def ByIFCPath(path, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5):
"""
Create a Graph from an IFC path. This code is partially based on code from Bruno Postle.
Parameters
----------
path : str
The input IFC file path.
includeTypes : list , optional
A list of IFC object types to include in the graph. The default is [] which means all object types are included.
excludeTypes : list , optional
A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded.
includeRels : list , optional
A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included.
excludeRels : list , optional
A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded.
xMin : float, optional
The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
yMin : float, optional
The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
zMin : float, optional
The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
xMax : float, optional
The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
yMax : float, optional
The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
zMax : float, optional
The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
Returns
-------
topologic_core.Graph
The created graph.
"""
try:
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.util.element
import ifcopenshell.util.shape
import ifcopenshell.geom
except:
print("Graph.ByIFCPath - Warning: Installing required ifcopenshell library.")
try:
os.system("pip install ifcopenshell")
except:
os.system("pip install ifcopenshell --user")
try:
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.util.element
import ifcopenshell.util.shape
import ifcopenshell.geom
print("Graph.ByIFCPath - Warning: ifcopenshell library installed correctly.")
except:
warnings.warn("Graph.ByIFCPath - Error: Could not import ifcopenshell. Please try to install ifcopenshell manually. Returning None.")
return None
if not path:
print("Graph.ByIFCPath - Error: the input path is not a valid path. Returning None.")
return None
ifc_file = ifcopenshell.open(path)
if not ifc_file:
print("Graph.ByIFCPath - Error: Could not open the IFC file. Returning None.")
return None
return Graph.ByIFCFile(ifc_file, includeTypes=includeTypes, excludeTypes=excludeTypes, includeRels=includeRels, excludeRels=excludeRels, xMin=xMin, yMin=yMin, zMin=zMin, xMax=xMax, yMax=yMax, zMax=zMax)
@staticmethod
def ByMeshData(vertices, edges, vertexDictionaries=None, edgeDictionaries=None, tolerance=0.0001):
"""
Creates a graph from the input mesh data
Parameters
----------
vertices : list
The list of [x, y, z] coordinates of the vertices/
edges : list
the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j].
vertexDictionaries : list , optional
The python dictionaries of the vertices (in the same order as the list of vertices).
edgeDictionaries : list , optional
The python dictionaries of the edges (in the same order as the list of edges).
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The created graph
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
g_vertices = []
for i, v in enumerate(vertices):
g_v = Vertex.ByCoordinates(v[0], v[1], v[2])
if not vertexDictionaries == None:
if isinstance(vertexDictionaries[i], dict):
d = Dictionary.ByPythonDictionary(vertexDictionaries[i])
else:
d = vertexDictionaries[i]
if not d == None:
if len(Dictionary.Keys(d)) > 0:
g_v = Topology.SetDictionary(g_v, d)
g_vertices.append(g_v)
g_edges = []
for i, e in enumerate(edges):
sv = g_vertices[e[0]]
ev = g_vertices[e[1]]
g_e = Edge.ByVertices([sv, ev], tolerance=tolerance)
if not edgeDictionaries == None:
if isinstance(edgeDictionaries[i], dict):
d = Dictionary.ByPythonDictionary(edgeDictionaries[i])
else:
d = edgeDictionaries[i]
if not d == None:
if len(Dictionary.Keys(d)) > 0:
g_e = Topology.SetDictionary(g_e, d)
g_edges.append(g_e)
return Graph.ByVerticesEdges(g_vertices, g_edges)
@staticmethod
def ByTopology(topology, direct=True, directApertures=False, viaSharedTopologies=False, viaSharedApertures=False, toExteriorTopologies=False, toExteriorApertures=False, toContents=False, toOutposts=False, idKey="TOPOLOGIC_ID", outpostsKey="outposts", useInternalVertex=True, storeBREP=False, tolerance=0.0001):
"""
Creates a graph.See https://en.wikipedia.org/wiki/Graph_(discrete_mathematics).
Parameters
----------
topology : topologic_core.Topology
The input topology.
direct : bool , optional
If set to True, connect the subtopologies directly with a single edge. The default is True.
directApertures : bool , optional
If set to True, connect the subtopologies directly with a single edge if they share one or more apertures. The default is False.
viaSharedTopologies : bool , optional
If set to True, connect the subtopologies via their shared topologies. The default is False.
viaSharedApertures : bool , optional
If set to True, connect the subtopologies via their shared apertures. The default is False.
toExteriorTopologies : bool , optional
If set to True, connect the subtopologies to their exterior topologies. The default is False.
toExteriorApertures : bool , optional
If set to True, connect the subtopologies to their exterior apertures. The default is False.
toContents : bool , optional
If set to True, connect the subtopologies to their contents. The default is False.
toOutposts : bool , optional
If set to True, connect the topology to the list specified in its outposts. The default is False.
idKey : str , optional
The key to use to find outpost by ID. It is case insensitive. The default is "TOPOLOGIC_ID".
outpostsKey : str , optional
The key to use to find the list of outposts. It is case insensitive. The default is "outposts".
useInternalVertex : bool , optional
If set to True, use an internal vertex to represent the subtopology. Otherwise, use its centroid. The default is False.
storeBREP : bool , optional
If set to True, store the BRep of the subtopology in its representative vertex. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The created graph.
"""
from topologicpy.Dictionary import Dictionary
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Cluster import Cluster
from topologicpy.Topology import Topology
from topologicpy.Aperture import Aperture
def mergeDictionaries(sources):
if isinstance(sources, list) == False:
sources = [sources]
sinkKeys = []
sinkValues = []
d = sources[0].GetDictionary()
if d != None:
stlKeys = d.Keys()
if len(stlKeys) > 0:
sinkKeys = d.Keys()
sinkValues = Dictionary.Values(d)
for i in range(1,len(sources)):
d = sources[i].GetDictionary()
if d == None:
continue
stlKeys = d.Keys()
if len(stlKeys) > 0:
sourceKeys = d.Keys()
for aSourceKey in sourceKeys:
if aSourceKey not in sinkKeys:
sinkKeys.append(aSourceKey)
sinkValues.append("")
for i in range(len(sourceKeys)):
index = sinkKeys.index(sourceKeys[i])
sourceValue = Dictionary.ValueAtKey(d, sourceKeys[i])
if sourceValue != None:
if sinkValues[index] != "":
if isinstance(sinkValues[index], list):
sinkValues[index].append(sourceValue)
else:
sinkValues[index] = [sinkValues[index], sourceValue]
else:
sinkValues[index] = sourceValue
if len(sinkKeys) > 0 and len(sinkValues) > 0:
return Dictionary.ByKeysValues(sinkKeys, sinkValues)
return None
def mergeDictionaries2(sources):
if isinstance(sources, list) == False:
sources = [sources]
sinkKeys = []
sinkValues = []
d = sources[0]
if d != None:
stlKeys = d.Keys()
if len(stlKeys) > 0:
sinkKeys = d.Keys()
sinkValues = Dictionary.Values(d)
for i in range(1,len(sources)):
d = sources[i]
if d == None:
continue
stlKeys = d.Keys()
if len(stlKeys) > 0:
sourceKeys = d.Keys()
for aSourceKey in sourceKeys:
if aSourceKey not in sinkKeys:
sinkKeys.append(aSourceKey)
sinkValues.append("")
for i in range(len(sourceKeys)):
index = sinkKeys.index(sourceKeys[i])
sourceValue = Dictionary.ValueAtKey(d, sourceKeys[i])
if sourceValue != None:
if sinkValues[index] != "":
if isinstance(sinkValues[index], list):
sinkValues[index].append(sourceValue)
else:
sinkValues[index] = [sinkValues[index], sourceValue]
else:
sinkValues[index] = sourceValue
if len(sinkKeys) > 0 and len(sinkValues) > 0:
return Dictionary.ByKeysValues(sinkKeys, sinkValues)
return None
def outpostsByID(topologies, ids, idKey="TOPOLOGIC_ID"):
returnList = []
idList = []
for t in topologies:
d = Topology.Dictionary(t)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == idKey.lower():
k = key
if k:
id = Dictionary.ValueAtKey(d, k)
else:
id = ""
idList.append(id)
for id in ids:
try:
index = idList.index(id)
except:
index = None
if index:
returnList.append(topologies[index])
return returnList
def processCellComplex(item):
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
edges = []
vertices = []
cellmat = []
if direct == True:
cells = []
_ = topology.Cells(None, cells)
# Create a matrix of zeroes
for i in range(len(cells)):
cellRow = []
for j in range(len(cells)):
cellRow.append(0)
cellmat.append(cellRow)
for i in range(len(cells)):
for j in range(len(cells)):
if (i != j) and cellmat[i][j] == 0:
cellmat[i][j] = 1
cellmat[j][i] = 1
sharedt = Topology.SharedFaces(cells[i], cells[j])
if len(sharedt) > 0:
if useInternalVertex == True:
v1 = Topology.InternalVertex(cells[i], tolerance=tolerance)
v2 = Topology.InternalVertex(cells[j], tolerance=tolerance)
else:
v1 = cells[i].CenterOfMass()
v2 = cells[j].CenterOfMass()
e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance)
mDict = mergeDictionaries(sharedt)
if not mDict == None:
keys = (Dictionary.Keys(mDict) or [])+["relationship"]
values = (Dictionary.Values(mDict) or [])+["Direct"]
else:
keys = ["relationship"]
values = ["Direct"]
mDict = Dictionary.ByKeysValues(keys, values)
if mDict:
e.SetDictionary(mDict)
edges.append(e)
if directApertures == True:
cellmat = []
cells = []
_ = topology.Cells(None, cells)
# Create a matrix of zeroes
for i in range(len(cells)):
cellRow = []
for j in range(len(cells)):
cellRow.append(0)
cellmat.append(cellRow)
for i in range(len(cells)):
for j in range(len(cells)):
if (i != j) and cellmat[i][j] == 0:
cellmat[i][j] = 1
cellmat[j][i] = 1
sharedt = Topology.SharedFaces(cells[i], cells[j])
if len(sharedt) > 0:
apertureExists = False
for x in sharedt:
apList = []
_ = x.Apertures(apList)
if len(apList) > 0:
apTopList = []
for ap in apList:
apTopList.append(ap.Topology())
apertureExists = True
break
if apertureExists:
if useInternalVertex == True:
v1 = Topology.InternalVertex(cells[i], tolerance=tolerance)
v2 = Topology.InternalVertex(cells[j], tolerance=tolerance)
else:
v1 = cells[i].CenterOfMass()
v2 = cells[j].CenterOfMass()
e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance)
mDict = mergeDictionaries(apTopList)
if mDict:
e.SetDictionary(mDict)
edges.append(e)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance=tolerance)
vcc = Topology.InternalVertex(topology, tolerance=tolerance)
else:
vop = Topology.CenterOfMass(outpost)
vcc = Topology.CenterOfMass(topology)
d1 = Topology.Dictionary(vcc)
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)])
d3 = mergeDictionaries2([d1, d2])
_ = vcc.SetDictionary(d3)
else:
_ = vcc.SetDictionary(d1)
vertices.append(vcc)
tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
cells = []
_ = topology.Cells(None, cells)
if any([viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents]):
for aCell in cells:
if useInternalVertex == True:
vCell = Topology.InternalVertex(aCell, tolerance=tolerance)
else:
vCell = aCell.CenterOfMass()
d1 = aCell.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aCell), Topology.Type(aCell), Topology.TypeAsString(aCell)])
d3 = mergeDictionaries2([d1, d2])
_ = vCell.SetDictionary(d3)
else:
_ = vCell.SetDictionary(d1)
vertices.append(vCell)
faces = []
_ = aCell.Faces(None, faces)
sharedTopologies = []
exteriorTopologies = []
sharedApertures = []
exteriorApertures = []
contents = []
_ = aCell.Contents(contents)
for aFace in faces:
cells1 = []
_ = aFace.Cells(topology, cells1)
if len(cells1) > 1:
sharedTopologies.append(aFace)
apertures = []
_ = aFace.Apertures(apertures)
for anAperture in apertures:
sharedApertures.append(anAperture)
else:
exteriorTopologies.append(aFace)
apertures = []
_ = aFace.Apertures(apertures)
for anAperture in apertures:
exteriorApertures.append(anAperture)
if viaSharedTopologies:
for sharedTopology in sharedTopologies:
if useInternalVertex == True:
vst = Topology.InternalVertex(sharedTopology, tolerance)
else:
vst = sharedTopology.CenterOfMass()
d1 = sharedTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = sharedTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
d1 = content.GetDictionary()
vst2 = Vertex.ByCoordinates(vst2.X(), vst2.Y(), vst2.Z())
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if viaSharedApertures:
for sharedAperture in sharedApertures:
sharedAp = sharedAperture.Topology()
if useInternalVertex == True:
vsa = Topology.InternalVertex(sharedAp, tolerance)
else:
vsa = sharedAp.CenterOfMass()
d1 = sharedAp.GetDictionary()
vsa = Vertex.ByCoordinates(vsa.X()+(tolerance*100), vsa.Y()+(tolerance*100), vsa.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)])
d3 = mergeDictionaries2([d1, d2])
_ = vsa.SetDictionary(d3)
else:
_ = vsa.SetDictionary(d1)
vertices.append(vsa)
tempe = Edge.ByStartVertexEndVertex(vCell, vsa, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorTopologies:
for exteriorTopology in exteriorTopologies:
if useInternalVertex == True:
vet = Topology.InternalVertex(exteriorTopology, tolerance)
else:
vet = exteriorTopology.CenterOfMass()
_ = vet.SetDictionary(exteriorTopology.GetDictionary())
d1 = exteriorTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vet.SetDictionary(d3)
else:
_ = vet.SetDictionary(d1)
vertices.append(vet)
tempe = Edge.ByStartVertexEndVertex(vCell, vet, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = exteriorTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
d1 = content.GetDictionary()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vet, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorApertures:
for exteriorAperture in exteriorApertures:
exTop = exteriorAperture.Topology()
if useInternalVertex == True:
vea = Topology.InternalVertex(exTop, tolerance)
else:
vea = exTop.CenterOfMass()
d1 = exTop.GetDictionary()
vea = Vertex.ByCoordinates(vea.X()+(tolerance*100), vea.Y()+(tolerance*100), vea.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)])
d3 = mergeDictionaries2([d1, d2])
_ = vea.SetDictionary(d3)
else:
_ = vea.SetDictionary(d1)
vertices.append(vea)
tempe = Edge.ByStartVertexEndVertex(vCell, vea, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = aCell.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vcn = Topology.InternalVertex(content, tolerance)
else:
vcn = content.CenterOfMass()
vcn = Vertex.ByCoordinates(vcn.X()+(tolerance*100), vcn.Y()+(tolerance*100), vcn.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vcn.SetDictionary(d3)
else:
_ = vcn.SetDictionary(d1)
vertices.append(vcn)
tempe = Edge.ByStartVertexEndVertex(vCell, vcn, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
for aCell in cells:
if useInternalVertex == True:
vCell = Topology.InternalVertex(aCell, tolerance=tolerance)
else:
vCell = aCell.CenterOfMass()
d1 = aCell.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aCell), Topology.Type(aCell), Topology.TypeAsString(aCell)])
d3 = mergeDictionaries2([d1, d2])
_ = vCell.SetDictionary(d3)
else:
_ = vCell.SetDictionary(d1)
vertices.append(vCell)
return [vertices,edges]
def processCell(item):
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
vertices = []
edges = []
if useInternalVertex == True:
vCell = Topology.InternalVertex(topology, tolerance=tolerance)
else:
vCell = topology.CenterOfMass()
d1 = topology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)])
d3 = mergeDictionaries2([d1, d2])
_ = vCell.SetDictionary(d3)
else:
_ = vCell.SetDictionary(d1)
vertices.append(vCell)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(d, k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance)
else:
vop = Topology.CenterOfMass(outpost)
tempe = Edge.ByStartVertexEndVertex(vCell, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if any([toExteriorTopologies, toExteriorApertures, toContents]):
faces = Topology.Faces(topology)
exteriorTopologies = []
exteriorApertures = []
for aFace in faces:
exteriorTopologies.append(aFace)
apertures = Topology.Apertures(aFace)
for anAperture in apertures:
exteriorApertures.append(anAperture)
if toExteriorTopologies:
for exteriorTopology in exteriorTopologies:
if useInternalVertex == True:
vst = Topology.InternalVertex(exteriorTopology, tolerance)
else:
vst = exteriorTopology.CenterOfMass()
d1 = exteriorTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = exteriorTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorApertures:
for exteriorAperture in exteriorApertures:
exTop = exteriorAperture.Topology()
if useInternalVertex == True:
vst = Topology.InternalVertex(exTop, tolerance)
else:
vst = exTop.CenterOfMass()
d1 = exTop.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = topology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst = Topology.InternalVertex(content, tolerance)
else:
vst = content.CenterOfMass()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
return [vertices, edges]
def processShell(item):
from topologicpy.Face import Face
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
graph = None
edges = []
vertices = []
facemat = []
if direct == True:
topFaces = []
_ = topology.Faces(None, topFaces)
# Create a matrix of zeroes
for i in range(len(topFaces)):
faceRow = []
for j in range(len(topFaces)):
faceRow.append(0)
facemat.append(faceRow)
for i in range(len(topFaces)):
for j in range(len(topFaces)):
if (i != j) and facemat[i][j] == 0:
facemat[i][j] = 1
facemat[j][i] = 1
sharedt = Topology.SharedEdges(topFaces[i], topFaces[j])
if len(sharedt) > 0:
if useInternalVertex == True:
v1 = Topology.InternalVertex(topFaces[i], tolerance=tolerance)
v2 = Topology.InternalVertex(topFaces[j], tolerance=tolerance)
else:
v1 = topFaces[i].CenterOfMass()
v2 = topFaces[j].CenterOfMass()
e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance)
mDict = mergeDictionaries(sharedt)
if not mDict == None:
keys = (Dictionary.Keys(mDict) or [])+["relationship"]
values = (Dictionary.Values(mDict) or [])+["Direct"]
else:
keys = ["relationship"]
values = ["Direct"]
mDict = Dictionary.ByKeysValues(keys, values)
if mDict:
e.SetDictionary(mDict)
edges.append(e)
if directApertures == True:
facemat = []
topFaces = []
_ = topology.Faces(None, topFaces)
# Create a matrix of zeroes
for i in range(len(topFaces)):
faceRow = []
for j in range(len(topFaces)):
faceRow.append(0)
facemat.append(faceRow)
for i in range(len(topFaces)):
for j in range(len(topFaces)):
if (i != j) and facemat[i][j] == 0:
facemat[i][j] = 1
facemat[j][i] = 1
sharedt = Topology.SharedEdges(topFaces[i], topFaces[j])
if len(sharedt) > 0:
apertureExists = False
for x in sharedt:
apList = []
_ = x.Apertures(apList)
if len(apList) > 0:
apertureExists = True
break
if apertureExists:
apTopList = []
for ap in apList:
apTopList.append(ap.Topology())
if useInternalVertex == True:
v1 = Topology.InternalVertex(topFaces[i], tolerance=tolerance)
v2 = Topology.InternalVertex(topFaces[j], tolerance=tolerance)
else:
v1 = topFaces[i].CenterOfMass()
v2 = topFaces[j].CenterOfMass()
e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance)
mDict = mergeDictionaries(apTopList)
if mDict:
e.SetDictionary(mDict)
edges.append(e)
topFaces = []
_ = topology.Faces(None, topFaces)
if any([viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents == True]):
for aFace in topFaces:
if useInternalVertex == True:
vFace = Topology.InternalVertex(aFace, tolerance=tolerance)
else:
vFace = aFace.CenterOfMass()
_ = vFace.SetDictionary(aFace.GetDictionary())
vertices.append(vFace)
fEdges = []
_ = aFace.Edges(None, fEdges)
sharedTopologies = []
exteriorTopologies = []
sharedApertures = []
exteriorApertures = []
for anEdge in fEdges:
faces = []
_ = anEdge.Faces(topology, faces)
if len(faces) > 1:
sharedTopologies.append(anEdge)
apertures = []
_ = anEdge.Apertures(apertures)
for anAperture in apertures:
sharedApertures.append(anAperture)
else:
exteriorTopologies.append(anEdge)
apertures = []
_ = anEdge.Apertures(apertures)
for anAperture in apertures:
exteriorApertures.append(anAperture)
if viaSharedTopologies:
for sharedTopology in sharedTopologies:
if useInternalVertex == True:
vst = Topology.InternalVertex(sharedTopology, tolerance)
else:
vst = sharedTopology.CenterOfMass()
d1 = sharedTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = sharedTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if viaSharedApertures:
for sharedAperture in sharedApertures:
sharedAp = Aperture.Topology(sharedAperture)
if useInternalVertex == True:
vst = Topology.InternalVertex(sharedAp, tolerance)
else:
vst = sharedAp.CenterOfMass()
d1 = sharedAp.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorTopologies:
for exteriorTopology in exteriorTopologies:
if useInternalVertex == True:
vst = Topology.InternalVertex(exteriorTopology, tolerance)
else:
vst = exteriorTopology.CenterOfMass()
d1 = exteriorTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = exteriorTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorApertures:
for exteriorAperture in exteriorApertures:
exTop = exteriorAperture.Topology()
if useInternalVertex == True:
vst = Topology.InternalVertex(exTop, tolerance)
else:
vst = exTop.CenterOfMass()
d1 = exTop.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = aFace.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst = Topology.InternalVertex(content, tolerance)
else:
vst = content.CenterOfMass()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
for aFace in topFaces:
if useInternalVertex == True:
vFace = Topology.InternalVertex(aFace, tolerance)
else:
vFace = aFace.CenterOfMass()
d1 = aFace.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aFace), Topology.Type(aFace), Topology.TypeAsString(aFace)])
d3 = mergeDictionaries2([d1, d2])
_ = vFace.SetDictionary(d3)
else:
_ = vFace.SetDictionary(d1)
vertices.append(vFace)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance)
vcc = Topology.InternalVertex(topology, tolerance)
else:
vop = Topology.CenterOfMass(outpost)
vcc = Topology.CenterOfMass(topology)
d1 = Topology.Dictionary(vcc)
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)])
d3 = mergeDictionaries2([d1, d2])
_ = vcc.SetDictionary(d3)
else:
_ = vcc.SetDictionary(d1)
vertices.append(vcc)
tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
return [vertices, edges]
def processFace(item):
from topologicpy.Face import Face
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
graph = None
vertices = []
edges = []
if useInternalVertex == True:
vFace = Topology.InternalVertex(topology, tolerance=tolerance)
else:
vFace = topology.CenterOfMass()
d1 = topology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)])
d3 = mergeDictionaries2([d1, d2])
_ = vFace.SetDictionary(d3)
else:
_ = vFace.SetDictionary(d1)
vertices.append(vFace)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(d, k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance)
else:
vop = Topology.CenterOfMass(outpost)
tempe = Edge.ByStartVertexEndVertex(vFace, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True):
fEdges = []
_ = topology.Edges(None, fEdges)
exteriorTopologies = []
exteriorApertures = []
for anEdge in fEdges:
exteriorTopologies.append(anEdge)
apertures = []
_ = anEdge.Apertures(apertures)
for anAperture in apertures:
exteriorApertures.append(anAperture)
if toExteriorTopologies:
for exteriorTopology in exteriorTopologies:
if useInternalVertex == True:
vst = Topology.InternalVertex(exteriorTopology, tolerance)
else:
vst = exteriorTopology.CenterOfMass()
d1 = exteriorTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = exteriorTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorApertures:
for exteriorAperture in exteriorApertures:
exTop = exteriorAperture.Topology()
if useInternalVertex == True:
vst = Topology.InternalVertex(exTop, tolerance)
else:
vst = exTop.CenterOfMass()
d1 = exTop.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = topology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst = Topology.InternalVertex(content, tolerance)
else:
vst = content.CenterOfMass()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
return [vertices, edges]
def processWire(item):
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
graph = None
edges = []
vertices = []
edgemat = []
if direct == True:
topEdges = []
_ = topology.Edges(None, topEdges)
# Create a matrix of zeroes
for i in range(len(topEdges)):
edgeRow = []
for j in range(len(topEdges)):
edgeRow.append(0)
edgemat.append(edgeRow)
for i in range(len(topEdges)):
for j in range(len(topEdges)):
if (i != j) and edgemat[i][j] == 0:
edgemat[i][j] = 1
edgemat[j][i] = 1
sharedt = Topology.SharedVertices(topEdges[i], topEdges[j])
if len(sharedt) > 0:
try:
v1 = Edge.VertexByParameter(topEdges[i], 0.5)
except:
v1 = topEdges[j].CenterOfMass()
try:
v2 = Edge.VertexByParameter(topEdges[j], 0.5)
except:
v2 = topEdges[j].CenterOfMass()
e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance)
mDict = mergeDictionaries(sharedt)
if not mDict == None:
keys = (Dictionary.Keys(mDict) or [])+["relationship"]
values = (Dictionary.Values(mDict) or [])+["Direct"]
else:
keys = ["relationship"]
values = ["Direct"]
mDict = Dictionary.ByKeysValues(keys, values)
if mDict:
e.SetDictionary(mDict)
edges.append(e)
if directApertures == True:
edgemat = []
topEdges = []
_ = topology.Edges(None, topEdges)
# Create a matrix of zeroes
for i in range(len(topEdges)):
edgeRow = []
for j in range(len(topEdges)):
edgeRow.append(0)
edgemat.append(edgeRow)
for i in range(len(topEdges)):
for j in range(len(topEdges)):
if (i != j) and edgemat[i][j] == 0:
edgemat[i][j] = 1
edgemat[j][i] = 1
sharedt = Topology.SharedVertices(topEdges[i], topEdges[j])
if len(sharedt) > 0:
apertureExists = False
for x in sharedt:
apList = []
_ = x.Apertures(apList)
if len(apList) > 0:
apertureExists = True
break
if apertureExists:
try:
v1 = Edge.VertexByParameter(topEdges[i], 0.5)
except:
v1 = topEdges[j].CenterOfMass()
try:
v2 = Edge.VertexByParameter(topEdges[j], 0.5)
except:
v2 = topEdges[j].CenterOfMass()
e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance)
apTopologies = []
for ap in apList:
apTopologies.append(ap.Topology())
mDict = mergeDictionaries(apTopologies)
if mDict:
e.SetDictionary(mDict)
edges.append(e)
topEdges = []
_ = topology.Edges(None, topEdges)
if (viaSharedTopologies == True) or (viaSharedApertures == True) or (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True):
for anEdge in topEdges:
try:
vEdge = Edge.VertexByParameter(anEdge, 0.5)
except:
vEdge = anEdge.CenterOfMass()
d1 = anEdge.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(anEdge), Topology.Type(anEdge), Topology.TypeAsString(anEdge)])
d3 = mergeDictionaries2([d1, d2])
_ = vEdge.SetDictionary(d3)
else:
_ = vEdge.SetDictionary(d1)
vertices.append(vEdge)
eVertices = []
_ = anEdge.Vertices(None, eVertices)
sharedTopologies = []
exteriorTopologies = []
sharedApertures = []
exteriorApertures = []
contents = []
_ = anEdge.Contents(contents)
for aVertex in eVertices:
tempEdges = []
_ = aVertex.Edges(topology, tempEdges)
if len(tempEdges) > 1:
sharedTopologies.append(aVertex)
apertures = []
_ = aVertex.Apertures(apertures)
for anAperture in apertures:
sharedApertures.append(anAperture)
else:
exteriorTopologies.append(aVertex)
apertures = []
_ = aVertex.Apertures(apertures)
for anAperture in apertures:
exteriorApertures.append(anAperture)
if viaSharedTopologies:
for sharedTopology in sharedTopologies:
vst = sharedTopology.CenterOfMass()
d1 = sharedTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = sharedTopology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if viaSharedApertures:
for sharedAperture in sharedApertures:
sharedAp = sharedAperture.Topology()
if useInternalVertex == True:
vst = Topology.InternalVertex(sharedAp, tolerance)
else:
vst = sharedAp.CenterOfMass()
d1 = sharedAp.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorTopologies:
for exteriorTopology in exteriorTopologies:
vst = exteriorTopology
vertices.append(exteriorTopology)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = vst.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorApertures:
for exteriorAperture in exteriorApertures:
exTop = exteriorAperture.Topology()
if useInternalVertex == True:
vst = Topology.InternalVertex(exTop, tolerance)
else:
vst = exTop.CenterOfMass()
d1 = exTop.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = anEdge.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst = Topology.InternalVertex(content, tolerance)
else:
vst = content.CenterOfMass()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
for anEdge in topEdges:
try:
vEdge = Edge.VertexByParameter(anEdge, 0.5)
except:
vEdge = anEdge.CenterOfMass()
d1 = anEdge.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(anEdge), Topology.Type(anEdge), Topology.TypeAsString(anEdge)])
d3 = mergeDictionaries2([d1, d2])
_ = vEdge.SetDictionary(d3)
else:
_ = vEdge.SetDictionary(d1)
vertices.append(vEdge)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance)
vcc = Topology.InternalVertex(topology, tolerance)
else:
vop = Topology.CenterOfMass(outpost)
vcc = Topology.CenterOfMass(topology)
d1 = Topology.Dictionary(vcc)
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)])
d3 = mergeDictionaries2([d1, d2])
_ = vcc.SetDictionary(d3)
else:
_ = vcc.SetDictionary(d1)
vertices.append(vcc)
tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
return [vertices, edges]
def processEdge(item):
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
graph = None
vertices = []
edges = []
if useInternalVertex == True:
try:
vEdge = Edge.VertexByParameter(topology, 0.5)
except:
vEdge = topology.CenterOfMass()
else:
vEdge = topology.CenterOfMass()
d1 = vEdge.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)])
d3 = mergeDictionaries2([d1, d2])
_ = vEdge.SetDictionary(d3)
else:
_ = vEdge.SetDictionary(topology.GetDictionary())
vertices.append(vEdge)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(d, k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance)
else:
vop = Topology.CenterOfMass(outpost)
tempe = Edge.ByStartVertexEndVertex(vEdge, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True):
eVertices = []
_ = topology.Vertices(None, eVertices)
exteriorTopologies = []
exteriorApertures = []
for aVertex in eVertices:
exteriorTopologies.append(aVertex)
apertures = []
_ = aVertex.Apertures(apertures)
for anAperture in apertures:
exteriorApertures.append(anAperture)
if toExteriorTopologies:
for exteriorTopology in exteriorTopologies:
if useInternalVertex == True:
vst = Topology.InternalVertex(exteriorTopology, tolerance)
else:
vst = exteriorTopology.CenterOfMass()
d1 = exteriorTopology.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toContents:
contents = []
_ = vst.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst2 = Topology.InternalVertex(content, tolerance)
else:
vst2 = content.CenterOfMass()
vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100))
d1 = content.GetDictionary()
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst2.SetDictionary(d3)
else:
_ = vst2.SetDictionary(d1)
vertices.append(vst2)
tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toExteriorApertures:
for exteriorAperture in exteriorApertures:
exTop = exteriorAperture.Topology()
if useInternalVertex == True:
vst = Topology.InternalVertex(exTop, tolerance)
else:
vst = exTop.CenterOfMass()
d1 = exTop.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
_ = vst.SetDictionary(exTop.GetDictionary())
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
return [vertices, edges]
def processVertex(item):
topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item
vertices = [topology]
edges = []
if toContents:
contents = []
_ = topology.Contents(contents)
for content in contents:
if Topology.IsInstance(content, "Aperture"):
content = Aperture.Topology(content)
if useInternalVertex == True:
vst = Topology.InternalVertex(content, tolerance)
else:
vst = content.CenterOfMass()
d1 = content.GetDictionary()
vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100))
if storeBREP:
d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)])
d3 = mergeDictionaries2([d1, d2])
_ = vst.SetDictionary(d3)
else:
_ = vst.SetDictionary(d1)
vertices.append(vst)
tempe = Edge.ByStartVertexEndVertex(topology, vst, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
if toOutposts and others:
d = Topology.Dictionary(topology)
if not d == None:
keys = Dictionary.Keys(d)
else:
keys = []
k = None
for key in keys:
if key.lower() == outpostsKey.lower():
k = key
if k:
ids = Dictionary.ValueAtKey(d, k)
outposts = outpostsByID(others, ids, idKey)
else:
outposts = []
for outpost in outposts:
if useInternalVertex == True:
vop = Topology.InternalVertex(outpost, tolerance)
else:
vop = Topology.CenterOfMass(outpost)
tempe = Edge.ByStartVertexEndVertex(topology, vop, tolerance=tolerance)
tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"])
_ = tempe.SetDictionary(tempd)
edges.append(tempe)
return [vertices, edges]
if not Topology.IsInstance(topology, "Topology"):
print("Graph.ByTopology - Error: The input topology is not a valid topology. Returning None.")
return None
graph = None
item = [topology, None, None, None, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, None, useInternalVertex, storeBREP, tolerance]
vertices = []
edges = []
if Topology.IsInstance(topology, "CellComplex"):
vertices, edges = processCellComplex(item)
elif Topology.IsInstance(topology, "Cell"):
vertices, edges = processCell(item)
elif Topology.IsInstance(topology, "Shell"):
vertices, edges = processShell(item)
elif Topology.IsInstance(topology, "Face"):
vertices, edges = processFace(item)
elif Topology.IsInstance(topology, "Wire"):
vertices, edges = processWire(item)
elif Topology.IsInstance(topology, "Edge"):
vertices, edges = processEdge(item)
elif Topology.IsInstance(topology, "Vertex"):
vertices, edges = processVertex(item)
elif Topology.IsInstance(topology, "Cluster"):
c_cellComplexes = Topology.CellComplexes(topology)
c_cells = Cluster.FreeCells(topology, tolerance=tolerance)
c_shells = Cluster.FreeShells(topology, tolerance=tolerance)
c_faces = Cluster.FreeFaces(topology, tolerance=tolerance)
c_wires = Cluster.FreeWires(topology, tolerance=tolerance)
c_edges = Cluster.FreeEdges(topology, tolerance=tolerance)
c_vertices = Cluster.FreeVertices(topology, tolerance=tolerance)
others = c_cellComplexes+c_cells+c_shells+c_faces+c_wires+c_edges+c_vertices
parameters = [others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance]
for t in c_cellComplexes:
v, e = processCellComplex([t]+parameters)
vertices += v
edges += e
for t in c_cells:
v, e = processCell([t]+parameters)
vertices += v
edges += e
for t in c_shells:
v, e = processShell([t]+parameters)
vertices += v
edges += e
for t in c_faces:
v, e = processFace([t]+parameters)
vertices += v
edges += e
for t in c_wires:
v, e = processWire([t]+parameters)
vertices += v
edges += e
for t in c_edges:
v, e = processEdge([t]+parameters)
vertices += v
edges += e
for t in c_vertices:
v, e = processVertex([t]+parameters)
vertices += v
edges += e
else:
return None
return Graph.ByVerticesEdges(vertices, edges)
@staticmethod
def ByVerticesEdges(vertices, edges):
"""
Creates a graph from the input list of vertices and edges.
Parameters
----------
vertices : list
The input list of vertices.
edges : list
The input list of edges.
Returns
-------
topologic_core.Graph
The created graph.
"""
from topologicpy.Topology import Topology
if not isinstance(vertices, list):
print("Graph.ByVerticesEdges - Error: The input list of vertices is not a valid list. Returning None.")
return None
if not isinstance(edges, list):
print("Graph.ByVerticesEdges - Error: The input list of edges is not a valid list. Returning None.")
return None
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
edges = [e for e in edges if Topology.IsInstance(e, "Edge")]
return topologic.Graph.ByVerticesEdges(vertices, edges) # Hook to Core
@staticmethod
def ChromaticNumber(graph, maxColors: int = 3, silent: bool = False):
"""
Returns the chromatic number of the input graph. See https://en.wikipedia.org/wiki/Graph_coloring.
Parameters
----------
graph : topologic_core.Graph
The input graph.
maxColors : int , optional
The desired maximum number of colors to test against. The default is 3.
silent : bool , optional
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
Returns
-------
int
The chromatic number of the input graph.
"""
# This is based on code from https://www.geeksforgeeks.org/graph-coloring-applications/
from topologicpy.Topology import Topology
def is_safe(graph, v, color, c):
for i in range(len(graph)):
if graph[v][i] == 1 and color[i] == c:
return False
return True
def graph_coloring(graph, m, color, v):
V = len(graph)
if v == V:
return True
for c in range(1, m + 1):
if is_safe(graph, v, color, c):
color[v] = c
if graph_coloring(graph, m, color, v + 1):
return True
color[v] = 0
return False
def chromatic_number(graph):
V = len(graph)
color = [0] * V
m = 1
while True:
if graph_coloring(graph, m, color, 0):
return m
m += 1
if not Topology.IsInstance(graph, "Graph"):
if not silent:
print("Graph.ChromaticNumber - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if maxColors < 1:
if not silent:
print("Graph.ChromaticNumber - Error: The input maxColors parameter is not a valid positive number. Returning None.")
return None
adj_matrix = Graph.AdjacencyMatrix(graph)
return chromatic_number(adj_matrix)
@staticmethod
def Color(graph, oldKey: str = "color", newKey: str = "color", maxColors: int = None, tolerance: float = 0.0001):
"""
Colors the input vertices within the input graph. The saved value is an integer rather than an actual color. See Color.ByValueInRange to convert to an actual color.
Any vertices that have been pre-colored will not be affected. See https://en.wikipedia.org/wiki/Graph_coloring.
Parameters
----------
graph : topologic_core.Graph
The input graph.
oldKey : str , optional
The existing dictionary key to use to read any pre-existing color information. The default is "color".
newKey : str , optional
The new dictionary key to use to write out new color information. The default is "color".
maxColors : int , optional
The desired maximum number of colors to use. If set to None, the chromatic number of the graph is used. The default is None.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph, but with its vertices colored.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Helper import Helper
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
import math
def is_safe(v, graph, colors, c):
# Check if the color 'c' is safe for the vertex 'v'
for i in range(len(graph)):
if graph[v][i] and c == colors[i]:
return False
return True
def graph_coloring_util(graph, m, colors, v):
# Base case: If all vertices are assigned a color, return true
if v == len(graph):
return True
# Try different colors for the current vertex 'v'
for c in range(1, m + 1):
# Check if assignment of color 'c' to 'v' is fine
if is_safe(v, graph, colors, c):
colors[v] = c
# Recur to assign colors to the rest of the vertices
if graph_coloring_util(graph, m, colors, v + 1):
return True
# If assigning color 'c' doesn't lead to a solution, remove it
colors[v] = 0
# If no color can be assigned to this vertex, return false
return False
def graph_coloring(graph, m, colors):
# Call graph_coloring_util() for vertex 0
if not graph_coloring_util(graph, m, colors, 0):
return None
return [x-1 for x in colors]
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Color - Error: The input graph is not a valid graph. Returning None.")
return None
vertices = Graph.Vertices(graph)
adj_mat = Graph.AdjacencyMatrix(graph)
# Isolate vertices that have pre-existing colors as they shouldn't affect graph coloring.
for i, v in enumerate(vertices):
d = Topology.Dictionary(v)
c = Dictionary.ValueAtKey(d, oldKey)
if not c == None:
adj_mat[i] = [0] * len(vertices)
for j in range(len(adj_mat)):
row = adj_mat[j]
row[i] = 0
temp_graph = Graph.ByAdjacencyMatrix(adj_mat)
# If the maximum number of colors are not provided, compute it using the graph's chromatic number.
if maxColors == None:
maxColors = Graph.ChromaticNumber(temp_graph)
colors = [0] * len(vertices)
colors = graph_coloring(adj_mat, maxColors, colors)
for i, v in enumerate(vertices):
d = Topology.Dictionary(v)
d = Dictionary.SetValueAtKey(d, newKey, colors[i])
v = Topology.SetDictionary(v, d)
return graph
@staticmethod
def ContractEdge(graph, edge, vertex=None, tolerance=0.0001):
"""
Contracts the input edge in the input graph into a single vertex. Please note that the dictionary of the edge is transferred to the
vertex that replaces it. See https://en.wikipedia.org/wiki/Edge_contraction
Parameters
----------
graph : topologic_core.Graph
The input graph.
edge : topologic_core.Edge
The input graph edge that needs to be contracted.
vertex : topollogic.Vertex , optional
The vertex to replace the contracted edge. If set to None, the centroid of the edge is chosen. The default is None.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph, but with input edge contracted into a single vertex.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
def OppositeVertex(edge, vertex, tolerance=0.0001):
sv = Edge.StartVertex(edge)
ev = Edge.EndVertex(edge)
d1 = Vertex.Distance(vertex, sv)
d2 = Vertex.Distance(vertex, ev)
if d1 < d2:
return [ev, 1]
return [sv, 0]
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ContractEdge - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(edge, "Edge"):
print("Graph.ContractEdge - Error: The input edge parameter is not a valid edge. Returning None.")
return None
if vertex == None:
vertex = Topology.Centroid(edge)
sv = Edge.StartVertex(edge)
ev = Edge.EndVertex(edge)
vd = Topology.Dictionary(vertex)
sd = Topology.Dictionary(sv)
dictionaries = []
keys = Dictionary.Keys(vd)
if isinstance(keys, list):
if len(keys) > 0:
dictionaries.append(vd)
keys = Dictionary.Keys(sd)
if isinstance(keys, list):
if len(keys) > 0:
dictionaries.append(sd)
ed = Topology.Dictionary(ev)
keys = Dictionary.Keys(ed)
if isinstance(keys, list):
if len(keys) > 0:
dictionaries.append(ed)
if len(dictionaries) == 1:
vertex = Topology.SetDictionary(vertex, dictionaries[0])
elif len(dictionaries) > 1:
cd = Dictionary.ByMergedDictionaries(dictionaries)
vertex = Topology.SetDictionary(vertex, cd)
graph = Graph.RemoveEdge(graph, edge, tolerance=tolerance)
graph = Graph.AddVertex(graph, vertex, tolerance=tolerance)
adj_edges_sv = Graph.Edges(graph, [sv])
adj_edges_ev = Graph.Edges(graph, [ev])
new_edges = []
for adj_edge_sv in adj_edges_sv:
ov, flag = OppositeVertex(adj_edge_sv, sv)
if flag == 0:
new_edge = Edge.ByVertices([ov, vertex])
else:
new_edge = Edge.ByVertices([vertex, ov])
d = Topology.Dictionary(adj_edge_sv)
keys = Dictionary.Keys(d)
if isinstance(keys, list):
if len(keys) > 0:
new_edge = Topology.SetDictionary(new_edge, d)
new_edges.append(new_edge)
for adj_edge_ev in adj_edges_ev:
ov, flag = OppositeVertex(adj_edge_ev, ev)
if flag == 0:
new_edge = Edge.ByVertices([ov, vertex])
else:
new_edge = Edge.ByVertices([vertex, ov])
d = Topology.Dictionary(adj_edge_ev)
keys = Dictionary.Keys(d)
if isinstance(keys, list):
if len(keys) > 0:
new_edge = Topology.SetDictionary(new_edge, d)
new_edges.append(new_edge)
for new_edge in new_edges:
graph = Graph.AddEdge(graph, new_edge, transferVertexDictionaries=True, transferEdgeDictionaries=True, tolerance=tolerance)
graph = Graph.RemoveVertex(graph,sv, tolerance=tolerance)
graph = Graph.RemoveVertex(graph,ev, tolerance=tolerance)
return graph
@staticmethod
def ClosenessCentrality(graph, vertices=None, tolerance = 0.0001):
"""
Return the closeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the closeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Closeness_centrality.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertices : list , optional
The input list of vertices. The default is None.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.")
return None
graphVertices = Graph.Vertices(graph)
if not isinstance(vertices, list):
vertices = graphVertices
else:
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
if len(vertices) < 1:
print("Graph.ClosenessCentrality - Error: The input list of vertices does not contain any valid vertices. Returning None.")
return None
n = len(graphVertices)
returnList = []
try:
for va in tqdm(vertices, desc="Computing Closeness Centrality", leave=False):
top_dist = 0
for vb in graphVertices:
if Topology.IsSame(va, vb):
d = 0
else:
d = Graph.TopologicalDistance(graph, va, vb, tolerance)
top_dist += d
if top_dist == 0:
returnList.append(0)
else:
returnList.append((n-1)/top_dist)
except:
print("Graph.ClosenessCentrality - Warning: Could not use tqdm.")
for va in vertices:
top_dist = 0
for vb in graphVertices:
if Topology.IsSame(va, vb):
d = 0
else:
d = Graph.TopologicalDistance(graph, va, vb, tolerance)
top_dist += d
if top_dist == 0:
returnList.append(0)
else:
returnList.append((n-1)/top_dist)
return returnList
@staticmethod
def Connect(graph, verticesA, verticesB, tolerance=0.0001):
"""
Connects the two lists of input vertices.
Parameters
----------
graph : topologic_core.Graph
The input graph.
verticesA : list
The first list of input vertices.
verticesB : topologic_core.Vertex
The second list of input vertices.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph with the connected input vertices.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Connect - Error: The input graph is not a valid graph. Returning None.")
return None
if not isinstance(verticesA, list):
print("Graph.Connect - Error: The input list of verticesA is not a valid list. Returning None.")
return None
if not isinstance(verticesB, list):
print("Graph.Connect - Error: The input list of verticesB is not a valid list. Returning None.")
return None
verticesA = [v for v in verticesA if Topology.IsInstance(v, "Vertex")]
verticesB = [v for v in verticesB if Topology.IsInstance(v, "Vertex")]
if len(verticesA) < 1:
print("Graph.Connect - Error: The input list of verticesA does not contain any valid vertices. Returning None.")
return None
if len(verticesB) < 1:
print("Graph.Connect - Error: The input list of verticesB does not contain any valid vertices. Returning None.")
return None
if not len(verticesA) == len(verticesB):
print("Graph.Connect - Error: The input lists verticesA and verticesB have different lengths. Returning None.")
return None
_ = graph.Connect(verticesA, verticesB, tolerance)
return graph
@staticmethod
def ContainsEdge(graph, edge, tolerance=0.0001):
"""
Returns True if the input graph contains the input edge. Returns False otherwise.
Parameters
----------
graph : topologic_core.Graph
The input graph.
edge : topologic_core.Edge
The input edge.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
bool
True if the input graph contains the input edge. False otherwise.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ContainsEdge - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(edge, "Edge"):
print("Graph.ContainsEdge - Error: The input edge is not a valid edge. Returning None.")
return None
return graph.ContainsEdge(edge, tolerance)
@staticmethod
def ContainsVertex(graph, vertex, tolerance=0.0001):
"""
Returns True if the input graph contains the input Vertex. Returns False otherwise.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input Vertex.
tolerance : float , optional
Ther desired tolerance. The default is 0.0001.
Returns
-------
bool
True if the input graph contains the input vertex. False otherwise.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ContainsVertex - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.ContainsVertex - Error: The input vertex is not a valid vertex. Returning None.")
return None
return graph.ContainsVertex(vertex, tolerance)
@staticmethod
def DegreeSequence(graph):
"""
Returns the degree sequence of the input graph. See https://mathworld.wolfram.com/DegreeSequence.html.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
list
The degree sequence of the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.DegreeSequence - Error: The input graph is not a valid graph. Returning None.")
return None
sequence = []
_ = graph.DegreeSequence(sequence)
return sequence
@staticmethod
def Density(graph):
"""
Returns the density of the input graph. See https://en.wikipedia.org/wiki/Dense_graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
float
The density of the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Density - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.Density()
@staticmethod
def DepthMap(graph, vertices=None, tolerance=0.0001):
"""
Return the depth map of the input list of vertices within the input graph. The returned list contains the total of the topological distances of each vertex to every other vertex in the input graph. The order of the depth map list is the same as the order of the input list of vertices. If no vertices are specified, the depth map of all the vertices in the input graph is computed.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertices : list , optional
The input list of vertices. The default is None.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The depth map of the input list of vertices within the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.DepthMap - Error: The input graph is not a valid graph. Returning None.")
return None
graphVertices = Graph.Vertices(graph)
if not isinstance(vertices, list):
vertices = graphVertices
else:
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
if len(vertices) < 1:
print("Graph.DepthMap - Error: The input list of vertices does not contain any valid vertices. Returning None.")
return None
depthMap = []
for va in vertices:
depth = 0
for vb in graphVertices:
if Topology.IsSame(va, vb):
dist = 0
else:
dist = Graph.TopologicalDistance(graph, va, vb, tolerance)
depth = depth + dist
depthMap.append(depth)
return depthMap
@staticmethod
def Diameter(graph):
"""
Returns the diameter of the input graph. See https://mathworld.wolfram.com/GraphDiameter.html.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
int
The diameter of the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Diameter - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.Diameter()
@staticmethod
def Dictionary(graph):
"""
Returns the dictionary of the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
topologic_core.Dictionary
The dictionary of the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Dictionary - Error: the input graph parameter is not a valid graph. Returning None.")
return None
return graph.GetDictionary()
@staticmethod
def Distance(graph, vertexA, vertexB, tolerance=0.0001):
"""
Returns the shortest-path distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory).
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
int
The shortest-path distance between the input vertices.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Distance - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.Distance - Error: The input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.Distance - Error: The input vertexB is not a valid vertex. Returning None.")
return None
return graph.TopologicalDistance(vertexA, vertexB, tolerance)
@staticmethod
def Edge(graph, vertexA, vertexB, tolerance=0.0001):
"""
Returns the edge in the input graph that connects in the input vertices.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input Vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Edge
The edge in the input graph that connects the input vertices.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Edge - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.Edge - Error: The input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.Edge - Error: The input vertexB is not a valid vertex. Returning None.")
return None
return graph.Edge(vertexA, vertexB, tolerance)
@staticmethod
def Edges(graph, vertices=None, tolerance=0.0001):
"""
Returns the edges found in the input graph. If the input list of vertices is specified, this method returns the edges connected to this list of vertices. Otherwise, it returns all graph edges.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertices : list , optional
An optional list of vertices to restrict the returned list of edges only to those connected to this list.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of edges in the graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Edges - Error: The input graph is not a valid graph. Returning None.")
return None
if not vertices:
edges = []
_ = graph.Edges(edges, tolerance)
return edges
else:
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
if len(vertices) < 1:
print("Graph.Edges - Error: The input list of vertices does not contain any valid vertices. Returning None.")
return None
edges = []
_ = graph.Edges(vertices, tolerance, edges)
return list(dict.fromkeys(edges)) # remove duplicates
@staticmethod
def ExportToAdjacencyMatrixCSV(adjacencyMatrix, path):
"""
Exports the input graph into a set of CSV files compatible with DGL.
Parameters
----------
path : str
The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved.
Returns
-------
bool
True if the graph has been successfully exported. False otherwise.
"""
# Convert the adjacency matrix (nested list) to a DataFrame
adjacency_matrix_df = pd.DataFrame(adjacencyMatrix)
# Export the DataFrame to a CSV file
try:
adjacency_matrix_df.to_csv(path, index=False, header=False)
return True
except:
return False
@staticmethod
def ExportToBOT(graph,
path,
format="turtle",
overwrite = False,
bidirectional=False,
includeAttributes=False,
includeLabel=False,
includeGeometry=False,
siteLabel = "Site_0001",
siteDictionary = None,
buildingLabel = "Building_0001",
buildingDictionary = None ,
storeyPrefix = "Storey",
floorLevels =[],
labelKey="label",
typeKey="type",
geometryKey="brep",
spaceType = "space",
wallType = "wall",
slabType = "slab",
doorType = "door",
windowType = "window",
contentType = "content",
):
"""
Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/.
Parameters
----------
graph : topologic_core.Graph
The input graph.
format : str , optional
The desired output format, the options are listed below. Thde default is "turtle".
turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks
xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0
json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants
ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded
n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things
trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs
trix : Trix , RDF/XML-like format for RDF quads
nquads : N-Quads , N-Triples-like format for RDF quads
path : str
The desired path to where the RDF/BOT file will be saved.
overwrite : bool , optional
If set to True, any existing file is overwritten. Otherwise, it is not. The default is False.
bidirectional : bool , optional
If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False.
includeAttributes : bool , optional
If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
includeLabel : bool , optional
If set to True, a label is attached to each node. Otherwise, it is not. The default is False.
includeGeometry : bool , optional
If set to True, the geometry associated with vertices in the graph are written out. Otherwise, it is not. The default is False.
siteLabel : str , optional
The desired site label. The default is "Site_0001".
siteDictionary : dict , optional
The dictionary of site attributes to include in the output. The default is None.
buildingLabel : str , optional
The desired building label. The default is "Building_0001".
buildingDictionary : dict , optional
The dictionary of building attributes to include in the output. The default is None.
storeyPrefix : str , optional
The desired prefixed to use for each building storey. The default is "Storey".
floorLevels : list , optional
The list of floor levels. This should be a numeric list, sorted from lowest to highest.
If not provided, floorLevels will be computed automatically based on the nodes' 'z' attribute.
typeKey : str , optional
The dictionary key to use to look up the type of the node. The default is "type".
labelKey : str , optional
The dictionary key to use to look up the label of the node. The default is "label".
geometryKey : str , optional
The dictionary key to use to look up the label of the node. The default is "brep".
spaceType : str , optional
The dictionary string value to use to look up nodes of type "space". The default is "space".
wallType : str , optional
The dictionary string value to use to look up nodes of type "wall". The default is "wall".
slabType : str , optional
The dictionary string value to use to look up nodes of type "slab". The default is "slab".
doorType : str , optional
The dictionary string value to use to look up nodes of type "door". The default is "door".
windowType : str , optional
The dictionary string value to use to look up nodes of type "window". The default is "window".
contentType : str , optional
The dictionary string value to use to look up nodes of type "content". The default is "contents".
format : str , optional
The desired output format, the options are listed below. Thde default is "turtle".
turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks
xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0
json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants
ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded
n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things
trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs
trix : Trix , RDF/XML-like format for RDF quads
nquads : N-Quads , N-Triples-like format for RDF quads
Returns
-------
str
The rdf graph serialized string using the BOT ontology.
"""
from os.path import exists
bot_graph = Graph.BOTGraph(graph,
bidirectional=bidirectional,
includeAttributes=includeAttributes,
includeLabel=includeLabel,
includeGeometry=includeGeometry,
siteLabel=siteLabel,
siteDictionary=siteDictionary,
buildingLabel=buildingLabel,
buildingDictionary=buildingDictionary,
storeyPrefix=storeyPrefix,
floorLevels=floorLevels,
labelKey=labelKey,
typeKey=typeKey,
geometryKey=geometryKey,
spaceType = spaceType,
wallType = wallType,
slabType = slabType,
doorType = doorType,
windowType = windowType,
contentType = contentType
)
if "turtle" in format.lower() or "ttl" in format.lower() or "turtle2" in format.lower():
ext = ".ttl"
elif "xml" in format.lower() or "pretty=xml" in format.lower() or "rdf/xml" in format.lower():
ext = ".xml"
elif "json" in format.lower():
ext = ".json"
elif "ntriples" in format.lower() or "nt" in format.lower() or "nt11" in format.lower():
ext = ".nt"
elif "n3" in format.lower() or "notation" in format.lower():
ext = ".n3"
elif "trig" in format.lower():
ext = ".trig"
elif "trix" in format.lower():
ext = ".trix"
elif "nquads" in format.lower():
ext = ".nquads"
else:
format = "turtle"
ext = ".ttl"
n = len(ext)
# Make sure the file extension is .brep
ext = path[len(path)-n:len(path)]
if ext.lower() != ext:
path = path+ext
if not overwrite and exists(path):
print("Graph.ExportToBOT - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
return None
status = False
try:
bot_graph.serialize(destination=path, format=format)
status = True
except:
status = False
return status
@staticmethod
def ExportToCSV(graph, path, graphLabel, graphFeatures="",
graphIDHeader="graph_id", graphLabelHeader="label", graphFeaturesHeader="feat",
edgeLabelKey="label", defaultEdgeLabel=0, edgeFeaturesKeys=[],
edgeSRCHeader="src_id", edgeDSTHeader="dst_id",
edgeLabelHeader="label", edgeFeaturesHeader="feat",
edgeTrainMaskHeader="train_mask", edgeValidateMaskHeader="val_mask", edgeTestMaskHeader="test_mask",
edgeMaskKey=None,
edgeTrainRatio=0.8, edgeValidateRatio=0.1, edgeTestRatio=0.1,
bidirectional=True,
nodeLabelKey="label", defaultNodeLabel=0, nodeFeaturesKeys=[],
nodeIDHeader="node_id", nodeLabelHeader="label", nodeFeaturesHeader="feat",
nodeTrainMaskHeader="train_mask", nodeValidateMaskHeader="val_mask", nodeTestMaskHeader="test_mask",
nodeMaskKey=None,
nodeTrainRatio=0.8, nodeValidateRatio=0.1, nodeTestRatio=0.1,
mantissa=6, overwrite=False):
"""
Exports the input graph into a set of CSV files compatible with DGL.
Parameters
----------
graph : topologic_core.Graph
The input graph
path : str
The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved.
graphLabel : float or int
The input graph label. This can be an int (categorical) or a float (continous)
graphFeatures : str , optional
The input graph features. This is a single string of numeric features separated by commas. Example: "3.456, 2.011, 56.4". The defauly is "".
graphIDHeader : str , optional
The desired graph ID column header. The default is "graph_id".
graphLabelHeader : str , optional
The desired graph label column header. The default is "label".
graphFeaturesHeader : str , optional
The desired graph features column header. The default is "feat".
edgeLabelKey : str , optional
The edge label dictionary key saved in each graph edge. The default is "label".
defaultEdgeLabel : int , optional
The default edge label to use if no edge label is found. The default is 0.
edgeSRCHeader : str , optional
The desired edge source column header. The default is "src_id".
edgeDSTHeader : str , optional
The desired edge destination column header. The default is "dst_id".
edgeFeaturesHeader : str , optional
The desired edge features column header. The default is "feat".
edgeFeaturesKeys : list , optional
The list of feature dictionary keys saved in the dicitonaries of edges. The default is [].
edgeTrainMaskHeader : str , optional
The desired edge train mask column header. The default is "train_mask".
edgeValidateMaskHeader : str , optional
The desired edge validate mask column header. The default is "val_mask".
edgeTestMaskHeader : str , optional
The desired edge test mask column header. The default is "test_mask".
edgeMaskKey : str , optional
The dictionary key where the edge train, validate, test category is to be found. The value should be 0 for train
1 for validate, and 2 for test. If no key is found, the ratio of train/validate/test will be used. The default is "mask".
edgeTrainRatio : float , optional
The desired ratio of the edge data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training.
This value is ignored if an edgeMaskKey is foud.
edgeValidateRatio : float , optional
The desired ratio of the edge data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation.
This value is ignored if an edgeMaskKey is foud.
edgeTestRatio : float , optional
The desired ratio of the edge data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing.
This value is ignored if an edgeMaskKey is foud.
bidirectional : bool , optional
If set to True, a reversed edge will also be saved for each edge in the graph. Otherwise, it will not. The default is True.
nodeFeaturesKeys : list , optional
The list of features keys saved in the dicitonaries of nodes. The default is [].
nodeLabelKey : str , optional
The node label dictionary key saved in each graph vertex. The default is "label".
defaultNodeLabel : int , optional
The default node label to use if no node label is found. The default is 0.
nodeIDHeader : str , optional
The desired node ID column header. The default is "node_id".
nodeLabelHeader : str , optional
The desired node label column header. The default is "label".
nodeFeaturesHeader : str , optional
The desired node features column header. The default is "feat".
nodeTrainMaskHeader : str , optional
The desired node train mask column header. The default is "train_mask".
nodeValidateMaskHeader : str , optional
The desired node validate mask column header. The default is "val_mask".
nodeTestMaskHeader : str , optional
The desired node test mask column header. The default is "test_mask".
nodeTrainRatio : float , optional
The desired ratio of the node data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training.
This value is ignored if an nodeMaskKey is foud.
nodeValidateRatio : float , optional
The desired ratio of the node data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation.
This value is ignored if an nodeMaskKey is foud.
nodeTestRatio : float , optional
The desired ratio of the node data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing.
This value is ignored if an nodeMaskKey is foud.
mantissa : int , optional
The desired length of the mantissa. The default is 6.
overwrite : bool , optional
If set to True, any existing files are overwritten. Otherwise, the input list of graphs is appended to the end of each file. The default is False.
Returns
-------
bool
True if the graph has been successfully exported. False otherwise.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Helper import Helper
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
import os
import math
import random
from os.path import exists
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ExportToCSV - Error: The input graph parameter is not a valid topologic graph. Returning None.")
return None
if not exists(path):
try:
os.mkdir(path)
except:
print("Graph.ExportToCSV - Error: Could not create a folder at the specified path parameter. Returning None.")
return None
if overwrite == False:
if not exists(os.path.join(path, "graphs.csv")):
print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.")
return None
if not exists(os.path.join(path, "edges.csv")):
print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.")
return None
if not exists(os.path.join(path, "nodes.csv")):
print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.")
return None
if abs(nodeTrainRatio + nodeValidateRatio + nodeTestRatio - 1) > 0.001:
print("Graph.ExportToCSV - Error: The node train, validate, test ratios do not add up to 1. Returning None")
return None
if abs(edgeTrainRatio + edgeValidateRatio + edgeTestRatio - 1) > 0.001:
print("Graph.ExportToCSV - Error: The edge train, validate, test ratios do not add up to 1. Returning None")
return None
# Step 1: Export Graphs
if overwrite == False:
graphs = pd.read_csv(os.path.join(path,"graphs.csv"))
max_id = max(list(graphs[graphIDHeader]))
graph_id = max_id + 1
else:
graph_id = 0
data = [[graph_id], [graphLabel], [graphFeatures]]
columns = [graphIDHeader, graphLabelHeader, graphFeaturesHeader]
# Write Graph Data to CSV file
data = Helper.Iterate(data)
data = Helper.Transpose(data)
df = pd.DataFrame(data, columns=columns)
if overwrite == False:
df.to_csv(os.path.join(path, "graphs.csv"), mode='a', index = False, header=False)
else:
df.to_csv(os.path.join(path, "graphs.csv"), mode='w+', index = False, header=True)
# Step 2: Export Nodes
vertices = Graph.Vertices(graph)
if len(vertices) < 3:
print("Graph.ExportToCSV - Error: The graph is too small to be used. Returning None")
return None
# Shuffle the vertices
vertices = random.sample(vertices, len(vertices))
node_train_max = math.floor(float(len(vertices))*nodeTrainRatio)
if node_train_max == 0:
node_train_max = 1
node_validate_max = math.floor(float(len(vertices))*nodeValidateRatio)
if node_validate_max == 0:
node_validate_max = 1
node_test_max = len(vertices) - node_train_max - node_validate_max
if node_test_max == 0:
node_test_max = 1
node_data = []
node_columns = [graphIDHeader, nodeIDHeader, nodeLabelHeader, nodeTrainMaskHeader, nodeValidateMaskHeader, nodeTestMaskHeader, nodeFeaturesHeader, "X", "Y", "Z"]
train = 0
test = 0
validate = 0
for i, v in enumerate(vertices):
# Get the node label
nd = Topology.Dictionary(v)
vLabel = Dictionary.ValueAtKey(nd, nodeLabelKey)
if vLabel == None:
vLabel = defaultNodeLabel
# Get the train/validate/test mask value
flag = False
if not nodeMaskKey == None:
if not nd == None:
keys = Dictionary.Keys(nd)
else:
keys = []
flag = True
if nodeMaskKey in keys:
value = Dictionary.ValueAtKey(nd, nodeMaskKey)
if not value in [0, 1, 2]:
flag = True
elif value == 0:
train_mask = True
validate_mask = False
test_mask = False
train = train + 1
elif value == 1:
train_mask = False
validate_mask = True
test_mask = False
validate = validate + 1
else:
train_mask = False
validate_mask = False
test_mask = True
test = test + 1
else:
flag = True
else:
flag = True
if flag:
if train < node_train_max:
train_mask = True
validate_mask = False
test_mask = False
train = train + 1
elif validate < node_validate_max:
train_mask = False
validate_mask = True
test_mask = False
validate = validate + 1
elif test < node_test_max:
train_mask = False
validate_mask = False
test_mask = True
test = test + 1
else:
train_mask = True
validate_mask = False
test_mask = False
train = train + 1
# Get the features of the vertex
node_features = ""
node_features_keys = Helper.Flatten(nodeFeaturesKeys)
for node_feature_key in node_features_keys:
if len(node_features) > 0:
node_features = node_features + ","+ str(round(float(Dictionary.ValueAtKey(nd, node_feature_key)),mantissa))
else:
node_features = str(round(float(Dictionary.ValueAtKey(nd, node_feature_key)),mantissa))
single_node_data = [graph_id, i, vLabel, train_mask, validate_mask, test_mask, node_features, round(float(Vertex.X(v)),mantissa), round(float(Vertex.Y(v)),mantissa), round(float(Vertex.Z(v)),mantissa)]
node_data.append(single_node_data)
# Write Node Data to CSV file
df = pd.DataFrame(node_data, columns= node_columns)
if graph_id == 0:
df.to_csv(os.path.join(path, "nodes.csv"), mode='w+', index = False, header=True)
else:
df.to_csv(os.path.join(path, "nodes.csv"), mode='a', index = False, header=False)
# Step 3: Export Edges
edge_data = []
edge_columns = [graphIDHeader, edgeSRCHeader, edgeDSTHeader, edgeLabelHeader, edgeTrainMaskHeader, edgeValidateMaskHeader, edgeTestMaskHeader, edgeFeaturesHeader]
train = 0
test = 0
validate = 0
edges = Graph.Edges(graph)
edge_train_max = math.floor(float(len(edges))*edgeTrainRatio)
edge_validate_max = math.floor(float(len(edges))*edgeValidateRatio)
edge_test_max = len(edges) - edge_train_max - edge_validate_max
for edge in edges:
# Get the edge label
ed = Topology.Dictionary(edge)
edge_label = Dictionary.ValueAtKey(ed, edgeLabelKey)
if edge_label == None:
edge_label = defaultEdgeLabel
# Get the train/validate/test mask value
flag = False
if not edgeMaskKey == None:
if not ed == None:
keys = Dictionary.Keys(ed)
else:
keys = []
flag = True
if edgeMaskKey in keys:
value = Dictionary.ValueAtKey(ed, edgeMaskKey)
if not value in [0, 1, 2]:
flag = True
elif value == 0:
train_mask = True
validate_mask = False
test_mask = False
train = train + 1
elif value == 1:
train_mask = False
validate_mask = True
test_mask = False
validate = validate + 1
else:
train_mask = False
validate_mask = False
test_mask = True
test = test + 1
else:
flag = True
else:
flag = True
if flag:
if train < edge_train_max:
train_mask = True
validate_mask = False
test_mask = False
train = train + 1
elif validate < edge_validate_max:
train_mask = False
validate_mask = True
test_mask = False
validate = validate + 1
elif test < edge_test_max:
train_mask = False
validate_mask = False
test_mask = True
test = test + 1
else:
train_mask = True
validate_mask = False
test_mask = False
train = train + 1
# Get the edge features
edge_features = ""
edge_features_keys = Helper.Flatten(edgeFeaturesKeys)
for edge_feature_key in edge_features_keys:
if len(edge_features) > 0:
edge_features = edge_features + ","+ str(round(float(Dictionary.ValueAtKey(ed, edge_feature_key)),mantissa))
else:
edge_features = str(round(float(Dictionary.ValueAtKey(ed, edge_feature_key)), mantissa))
# Get the Source and Destination vertex indices
src = Vertex.Index(Edge.StartVertex(edge), vertices)
dst = Vertex.Index(Edge.EndVertex(edge), vertices)
single_edge_data = [graph_id, src, dst, edge_label, train_mask, validate_mask, test_mask, edge_features]
edge_data.append(single_edge_data)
if bidirectional == True:
single_edge_data = [graph_id, src, dst, edge_label, train_mask, validate_mask, test_mask, edge_features]
edge_data.append(single_edge_data)
df = pd.DataFrame(edge_data, columns=edge_columns)
if graph_id == 0:
df.to_csv(os.path.join(path, "edges.csv"), mode='w+', index = False, header=True)
else:
df.to_csv(os.path.join(path, "edges.csv"), mode='a', index = False, header=False)
# Write out the meta.yaml file
yaml_file = open(os.path.join(path,"meta.yaml"), "w")
if graph_id > 0:
yaml_file.write('dataset_name: topologic_dataset\nedge_data:\n- file_name: edges.csv\nnode_data:\n- file_name: nodes.csv\ngraph_data:\n file_name: graphs.csv')
else:
yaml_file.write('dataset_name: topologic_dataset\nedge_data:\n- file_name: edges.csv\nnode_data:\n- file_name: nodes.csv')
yaml_file.close()
return True
@staticmethod
def ExportToGEXF(graph, path, graphWidth=20, graphLength=20, graphHeight=20,
defaultVertexColor="black", defaultVertexSize=3,
vertexLabelKey=None, vertexColorKey=None, vertexSizeKey=None,
defaultEdgeColor="black", defaultEdgeWeight=1, defaultEdgeType="undirected",
edgeLabelKey=None, edgeColorKey=None, edgeWeightKey=None, overwrite=False):
"""
Exports the input graph to a Graph Exchange XML (GEXF) file format. See https://gexf.net/
Parameters
----------
graph : topologic_core.Graph
The input graph
path : str
The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved.
graphWidth : float or int , optional
The desired graph width. The default is 20.
graphLength : float or int , optional
The desired graph length. The default is 20.
graphHeight : float or int , optional
The desired graph height. The default is 20.
defaultVertexColor : str , optional
The desired default vertex color. The default is "black".
defaultVertexSize : float or int , optional
The desired default vertex size. The default is 3.
defaultEdgeColor : str , optional
The desired default edge color. The default is "black".
defaultEdgeWeight : float or int , optional
The desired default edge weight. The edge weight determines the width of the displayed edge. The default is 3.
defaultEdgeType : str , optional
The desired default edge type. This can be one of "directed" or "undirected". The default is "undirected".
vertexLabelKey : str , optional
If specified, the vertex dictionary is searched for this key to determine the vertex label. If not specified
the vertex label being is set to "Node X" where is X is a unique number. The default is None.
vertexColorKey : str , optional
If specified, the vertex dictionary is searched for this key to determine the vertex color. If not specified
the vertex color is set to the value defined by defaultVertexColor parameter. The default is None.
vertexSizeKey : str , optional
If specified, the vertex dictionary is searched for this key to determine the vertex size. If not specified
the vertex size is set to the value defined by defaultVertexSize parameter. The default is None.
edgeLabelKey : str , optional
If specified, the edge dictionary is searched for this key to determine the edge label. If not specified
the edge label being is set to "Edge X" where is X is a unique number. The default is None.
edgeColorKey : str , optional
If specified, the edge dictionary is searched for this key to determine the edge color. If not specified
the edge color is set to the value defined by defaultEdgeColor parameter. The default is None.
edgeWeightKey : str , optional
If specified, the edge dictionary is searched for this key to determine the edge weight. If not specified
the edge weight is set to the value defined by defaultEdgeWeight parameter. The default is None.
overwrite : bool , optional
If set to True, any existing file is overwritten. Otherwise, it is not. The default is False.
Returns
-------
bool
True if the graph has been successfully exported. False otherwise.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
from topologicpy.Color import Color
import numbers
from datetime import datetime
import os
from os.path import exists
def create_gexf_file(nodes, edges, default_edge_type, node_attributes, edge_attributes, path):
with open(path, 'w') as file:
# Write the GEXF header
formatted_date = datetime.now().strftime("%Y-%m-%d")
if not isinstance(default_edge_type, str):
default_edge_type = "undirected"
if default_edge_type.lower() == "directed":
defaultedge_type = "directed"
else:
default_edge_type = "undirected"
file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
file.write('<gexf version="1.3" xmlns="http://www.gephi.org/gexf" xmlns:viz="http://www.gephi.org/gexf/viz">\n')
file.write(f'<meta lastmodifieddate="{formatted_date}">\n')
file.write('<creator>Topologic GEXF Generator</creator>\n')
file.write('<title>"Topologic Graph"</title>\n')
file.write('<description>"This is a Topologic Graph"</description>\n')
file.write('</meta>\n')
file.write(f'<graph type="static" defaultedgetype="{defaultEdgeType}">\n')
# Write attribute definitions
file.write('<attributes class="node" mode="static">\n')
for attr_name, attr_type in node_attributes.items():
file.write(f'<attribute id="{attr_name}" title="{attr_name}" type="{attr_type}"/>\n')
file.write('</attributes>\n')
# Write attribute definitions
file.write('<attributes class="edge" mode="static">\n')
for attr_name, attr_type in edge_attributes.items():
file.write(f'<attribute id="{attr_name}" title="{attr_name}" type="{attr_type}"/>\n')
file.write('</attributes>\n')
# Write nodes with attributes
file.write('<nodes>\n')
for node_id, node_attrs in nodes.items():
file.write(f'<node id="{node_id}" label="{node_attrs["label"]}">\n')
if "r" in node_attrs and "g" in node_attrs and "b" in node_attrs:
r = node_attrs['r']
g = node_attrs['g']
b = node_attrs['b']
file.write(f'<viz:color r="{r}" g="{g}" b="{b}"/>\n')
if "size" in node_attrs:
file.write(f'<viz:size value="{node_attrs["size"]}"/>\n')
if "x" in node_attrs and "y" in node_attrs and "z" in node_attrs:
file.write(f'<viz:position x="{node_attrs["x"]}" y="{node_attrs["y"]}" z="{node_attrs["z"]}"/>\n')
file.write('<attvalues>\n')
keys = node_attrs.keys()
for key in keys:
file.write(f'<attvalue id="{key}" value="{node_attrs[key]}"/>\n')
file.write('</attvalues>\n')
file.write('</node>\n')
file.write('</nodes>\n')
# Write edges with attributes
file.write('<edges>\n')
for edge_id, edge_attrs in edges.items():
source, target = edge_id
file.write(f'<edge id="{edge_id}" source="{source}" target="{target}" label="{edge_attrs["label"]}">\n')
if "color" in edge_attrs:
r, g, b = Color.ByCSSNamedColor(edge_attrs["color"])
file.write(f'<viz:color r="{r}" g="{g}" b="{b}"/>\n')
file.write('<attvalues>\n')
keys = edge_attrs.keys()
for key in keys:
file.write(f'<attvalue id="{key}" value="{edge_attrs[key]}"/>\n')
file.write('</attvalues>\n')
file.write('</edge>\n')
file.write('</edges>\n')
# Write the GEXF footer
file.write('</graph>\n')
file.write('</gexf>\n')
def valueType(value):
if isinstance(value, str):
return 'string'
elif isinstance(value, float):
return 'double'
elif isinstance(value, int):
return 'integer'
else:
return 'string'
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ExportToGEXF - Error: the input graph parameter is not a valid graph. Returning None.")
return None
if not isinstance(path, str):
print("Graph.ExportToGEXF - Error: the input path parameter is not a valid string. Returning None.")
return None
# Make sure the file extension is .brep
ext = path[len(path)-5:len(path)]
if ext.lower() != ".gexf":
path = path+".gexf"
if not overwrite and exists(path):
print("Graph.ExportToGEXF - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
return None
g_vertices = Graph.Vertices(graph)
g_edges = Graph.Edges(graph)
node_attributes = {'id': 'integer',
'label': 'string',
'x': 'double',
'y': 'double',
'z': 'double',
'r': 'integer',
'g': 'integer',
'b': 'integer',
'color': 'string',
'size': 'double'}
nodes = {}
# Resize the graph
xList = [Vertex.X(v) for v in g_vertices]
yList = [Vertex.Y(v) for v in g_vertices]
zList = [Vertex.Z(v) for v in g_vertices]
xMin = min(xList)
xMax = max(xList)
yMin = min(yList)
yMax = max(yList)
zMin = min(zList)
zMax = max(zList)
width = max(abs(xMax - xMin), 0.01)
length = max(abs(yMax - yMin), 0.01)
height = max(abs(zMax - zMin), 0.01)
x_sf = graphWidth/width
y_sf = graphLength/length
z_sf = graphHeight/height
x_avg = sum(xList)/float(len(xList))
y_avg = sum(yList)/float(len(yList))
z_avg = sum(zList)/float(len(zList))
for i, v in enumerate(g_vertices):
node_dict = {}
d = Topology.Dictionary(v)
keys = Dictionary.Keys(d)
values = Dictionary.Values(d)
x = (Vertex.X(v) - x_avg)*x_sf + x_avg
y = (Vertex.Y(v) - y_avg)*y_sf + y_avg
z = (Vertex.Z(v) - z_avg)*z_sf + z_avg
node_dict['x'] = x
node_dict['y'] = y
node_dict['z'] = z
node_dict['id'] = i
for m, key in enumerate(keys):
if key == "psets": #We cannot handle IFC psets at this point.
continue
if key == "id":
key = "TOPOLOGIC_ID"
if not key in node_attributes.keys():
node_attributes[key] = valueType(values[m])
if isinstance(values[m], str):
values[m] = values[m].replace('&','&')
values[m] = values[m].replace('<','<')
values[m] = values[m].replace('>','>')
values[m] = values[m].replace('"','"')
values[m] = values[m].replace('\'',''')
node_dict[key] = values[m]
dict_color = None
if not defaultVertexColor in Color.CSSNamedColors():
defaultVertexColor = "black"
vertex_color = defaultVertexColor
if isinstance(vertexColorKey, str):
dict_color = Dictionary.ValueAtKey(d, vertexColorKey)
if not dict_color == None:
vertex_color = dict_color
if not vertex_color in Color.CSSNamedColors():
vertex_color = defaultVertexColor
node_dict['color'] = vertex_color
r, g, b = Color.ByCSSNamedColor(vertex_color)
node_dict['r'] = r
node_dict['g'] = g
node_dict['b'] = b
dict_size = None
if isinstance(vertexSizeKey, str):
dict_size = Dictionary.ValueAtKey(d, vertexSizeKey)
vertex_size = defaultVertexSize
if not dict_size == None:
if isinstance(dict_size, numbers.Real):
vertex_size = dict_size
if not isinstance(vertex_size, numbers.Real):
vertex_size = defaultVertexSize
node_dict['size'] = vertex_size
vertex_label = "Node "+str(i)
if isinstance(vertexLabelKey, str):
vertex_label = Dictionary.ValueAtKey(d, vertexLabelKey)
if not isinstance(vertex_label, str):
vertex_label = "Node "+str(i)
if isinstance(vertex_label, str):
vertex_label = vertex_label.replace('&','&')
vertex_label = vertex_label.replace('<','<')
vertex_label = vertex_label.replace('>','>')
vertex_label = vertex_label.replace('"','"')
vertex_label = vertex_label.replace('\'',''')
node_dict['label'] = vertex_label
nodes[i] = node_dict
edge_attributes = {'id': 'integer',
'label': 'string',
'source': 'integer',
'target': 'integer',
'r': 'integer',
'g': 'integer',
'b': 'integer',
'color': 'string',
'weight': 'double'}
edges = {}
for i, edge in enumerate(g_edges):
edge_dict = {}
d = Topology.Dictionary(edge)
keys = Dictionary.Keys(d)
values = Dictionary.Values(d)
edge_dict['id'] = i
for m, key in enumerate(keys):
if key == "id":
key = "TOPOLOGIC_ID"
if not key in edge_attributes.keys():
edge_attributes[key] = valueType(values[m])
edge_dict[key] = values[m]
dict_color = None
if not defaultEdgeColor in Color.CSSNamedColors():
defaultEdgeColor = "black"
edge_color = defaultEdgeColor
if isinstance(edgeColorKey, str):
dict_color = Dictionary.ValueAtKey(d, edgeColorKey)
if not dict_color == None:
edge_color = dict_color
if not vertex_color in Color.CSSNamedColors():
edge_color = defaultVertexColor
edge_dict['color'] = edge_color
r, g, b = Color.ByCSSNamedColor(edge_color)
edge_dict['r'] = r
edge_dict['g'] = g
edge_dict['b'] = b
dict_weight = None
if not isinstance(defaultEdgeWeight, numbers.Real):
defaultEdgeWeight = 1
edge_weight = defaultEdgeWeight
if isinstance(edgeWeightKey, str):
dict_weight = Dictionary.ValueAtKey(d, edgeWeightKey)
if not dict_weight == None:
if isinstance(dict_weight, numbers.Real):
edge_weight = dict_weight
if not isinstance(edge_weight, numbers.Real):
edge_weight = defaultEdgeWeight
edge_dict['weight'] = edge_weight
sv = g_vertices[Vertex.Index(Edge.StartVertex(edge), g_vertices)]
ev = g_vertices[Vertex.Index(Edge.EndVertex(edge), g_vertices)]
svid = Vertex.Index(sv, g_vertices)
edge_dict['source'] = svid
evid = Vertex.Index(ev, g_vertices)
edge_dict['target'] = evid
edge_label = "Edge "+str(svid)+"-"+str(evid)
if isinstance(edgeLabelKey, str):
edge_label = Dictionary.ValueAtKey(d, edgeLabelKey)
if not isinstance(edge_label, str):
edge_label = "Edge "+str(svid)+"-"+str(evid)
edge_dict['label'] = edge_label
edges[(str(svid), str(evid))] = edge_dict
create_gexf_file(nodes, edges, defaultEdgeType, node_attributes, edge_attributes, path)
return True
@staticmethod
def ExportToJSON(graph, path, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", xKey="x", yKey="y", zKey="z", indent=4, sortKeys=False, mantissa=6, overwrite=False):
"""
Exports the input graph to a JSON file.
Parameters
----------
graph : topologic_core.Graph
The input graph.
path : str
The path to the JSON file.
verticesKey : str , optional
The desired key name to call vertices. The default is "vertices".
edgesKey : str , optional
The desired key name to call edges. The default is "edges".
vertexLabelKey : str , optional
If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number.
Note: If vertex labels are not unique, they will be forced to be unique.
edgeLabelKey : str , optional
If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number.
Note: If edge labels are not unique, they will be forced to be unique.
xKey : str , optional
The desired key name to use for x-coordinates. The default is "x".
yKey : str , optional
The desired key name to use for y-coordinates. The default is "y".
zKey : str , optional
The desired key name to use for z-coordinates. The default is "z".
indent : int , optional
The desired amount of indent spaces to use. The default is 4.
sortKeys : bool , optional
If set to True, the keys will be sorted. Otherwise, they won't be. The default is False.
mantissa : int , optional
The desired length of the mantissa. The default is 6.
overwrite : bool , optional
If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
Returns
-------
bool
The status of exporting the JSON file. If True, the operation was successful. Otherwise, it was unsuccesful.
"""
import json
from os.path import exists
# Make sure the file extension is .json
ext = path[len(path)-5:len(path)]
if ext.lower() != ".json":
path = path+".json"
if not overwrite and exists(path):
print("Graph.ExportToJSON - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
return None
f = None
try:
if overwrite == True:
f = open(path, "w")
else:
f = open(path, "x") # Try to create a new File
except:
raise Exception("Graph.ExportToJSON - Error: Could not create a new file at the following location: "+path)
if (f):
jsondata = Graph.JSONData(graph, verticesKey=verticesKey, edgesKey=edgesKey, vertexLabelKey=vertexLabelKey, edgeLabelKey=edgeLabelKey, xKey=xKey, yKey=yKey, zKey=zKey, mantissa=mantissa)
if jsondata != None:
json.dump(jsondata, f, indent=indent, sort_keys=sortKeys)
f.close()
return True
else:
f.close()
return False
return False
@staticmethod
def Flatten(graph, layout="spring", k=0.8, seed=None, iterations=50, rootVertex=None, tolerance=0.0001):
"""
Flattens the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
layout : str , optional
The desired mode for flattening. If set to 'spring', the algorithm uses a simplified version of the Fruchterman-Reingold force-directed algorithm to flatten and distribute the vertices.
If set to 'radial', the nodes will be distributed along a circle.
If set to 'tree', the nodes will be distributed using the Reingold-Tillford layout. The default is 'spring'.
k : float, optional
The desired spring constant to use for the attractive and repulsive forces. The default is 0.8.
seed : int , optional
The desired random seed to use. The default is None.
iterations : int , optional
The desired maximum number of iterations to solve the forces in the 'spring' mode. The default is 50.
rootVertex : topologic_core.Vertex , optional
The desired vertex to use as the root of the tree and radial layouts.
Returns
-------
topologic_core.Graph
The flattened graph.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Topology import Topology
import numpy as np
def buchheim(tree):
dt = firstwalk(_DrawTree(tree))
min = second_walk(dt)
if min < 0:
third_walk(dt, -min)
return dt
def third_walk(tree, n):
tree.x += n
for c in tree.children:
third_walk(c, n)
def firstwalk(v, distance=1.0):
if len(v.children) == 0:
if v.lmost_sibling:
v.x = v.lbrother().x + distance
else:
v.x = 0.0
else:
default_ancestor = v.children[0]
for w in v.children:
firstwalk(w)
default_ancestor = apportion(w, default_ancestor, distance)
execute_shifts(v)
midpoint = (v.children[0].x + v.children[-1].x) / 2
w = v.lbrother()
if w:
v.x = w.x + distance
v.mod = v.x - midpoint
else:
v.x = midpoint
return v
def apportion(v, default_ancestor, distance):
w = v.lbrother()
if w is not None:
vir = vor = v
vil = w
vol = v.lmost_sibling
sir = sor = v.mod
sil = vil.mod
sol = vol.mod
while vil.right() and vir.left():
vil = vil.right()
vir = vir.left()
vol = vol.left()
vor = vor.right()
vor.ancestor = v
shift = (vil.x + sil) - (vir.x + sir) + distance
if shift > 0:
move_subtree(ancestor(vil, v, default_ancestor), v, shift)
sir = sir + shift
sor = sor + shift
sil += vil.mod
sir += vir.mod
sol += vol.mod
sor += vor.mod
if vil.right() and not vor.right():
vor.thread = vil.right()
vor.mod += sil - sor
else:
if vir.left() and not vol.left():
vol.thread = vir.left()
vol.mod += sir - sol
default_ancestor = v
return default_ancestor
def move_subtree(wl, wr, shift):
subtrees = wr.number - wl.number
wr.change -= shift / subtrees
wr.shift += shift
wl.change += shift / subtrees
wr.x += shift
wr.mod += shift
def execute_shifts(v):
shift = change = 0
for w in v.children[::-1]:
w.x += shift
w.mod += shift
change += w.change
shift += w.shift + change
def ancestor(vil, v, default_ancestor):
if vil.ancestor in v.parent.children:
return vil.ancestor
else:
return default_ancestor
def second_walk(v, m=0, depth=0, min=None):
v.x += m
v.y = depth
if min is None or v.x < min:
min = v.x
for w in v.children:
min = second_walk(w, m + v.mod, depth + 1, min)
return min
def edge_list_to_adjacency_matrix(edge_list):
"""Converts an edge list to an adjacency matrix.
Args:
edge_list: A list of tuples, where each tuple is an edge.
Returns:
A numpy array representing the adjacency matrix.
"""
# Get the number of nodes from the edge list.
num_nodes = max([max(edge) for edge in edge_list]) + 1
# Create an adjacency matrix.
adjacency_matrix = np.zeros((num_nodes, num_nodes))
# Fill in the adjacency matrix.
for edge in edge_list:
adjacency_matrix[edge[0], edge[1]] = 1
adjacency_matrix[edge[1], edge[0]] = 1
return adjacency_matrix
def tree_from_edge_list(edge_list, root_index=0):
adj_matrix = edge_list_to_adjacency_matrix(edge_list)
num_nodes = adj_matrix.shape[0]
root = _Tree(str(root_index))
is_visited = np.zeros(num_nodes)
is_visited[root_index] = 1
old_roots = [root]
new_roots = []
while(np.sum(is_visited) < num_nodes):
new_roots = []
for temp_root in old_roots:
children = []
for i in range(num_nodes):
if adj_matrix[int(temp_root.node), i] == 1 and is_visited[i] == 0:
is_visited[i] = 1
child = _Tree(str(i))
temp_root.children.append(child)
children.append(child)
new_roots.extend(children)
old_roots = new_roots
return root, num_nodes
def spring_layout(edge_list, iterations=500, k=None, seed=None):
# Compute the layout of a graph using the Fruchterman-Reingold algorithm
# with a force-directed layout approach.
adj_matrix = edge_list_to_adjacency_matrix(edge_list)
# Set the random seed
if seed is not None:
np.random.seed(seed)
# Set the optimal distance between nodes
if k is None or k <= 0:
k = np.sqrt(1.0 / adj_matrix.shape[0])
# Initialize the positions of the nodes randomly
pos = np.random.rand(adj_matrix.shape[0], 2)
# Compute the initial temperature
t = 0.1 * np.max(pos)
# Compute the cooling factor
cooling_factor = t / iterations
# Iterate over the specified number of iterations
for i in range(iterations):
# Compute the distance between each pair of nodes
delta = pos[:, np.newaxis, :] - pos[np.newaxis, :, :]
distance = np.linalg.norm(delta, axis=-1)
# Avoid division by zero
distance = np.where(distance == 0, 0.1, distance)
# Compute the repulsive force between each pair of nodes
repulsive_force = k ** 2 / distance ** 2
# Compute the attractive force between each pair of adjacent nodes
attractive_force = adj_matrix * distance / k
# Compute the total force acting on each node
force = np.sum((repulsive_force - attractive_force)[:, :, np.newaxis] * delta, axis=1)
# Compute the displacement of each node
displacement = t * force / np.linalg.norm(force, axis=1)[:, np.newaxis]
# Update the positions of the nodes
pos += displacement
# Cool the temperature
t -= cooling_factor
return pos
def tree_layout(edge_list, root_index=0):
root, num_nodes = tree_from_edge_list(edge_list, root_index)
dt = buchheim(root)
pos = np.zeros((num_nodes, 2))
pos[int(dt.tree.node), 0] = dt.x
pos[int(dt.tree.node), 1] = dt.y
old_roots = [dt]
new_roots = []
while(len(old_roots) > 0):
new_roots = []
for temp_root in old_roots:
children = temp_root.children
for child in children:
pos[int(child.tree.node), 0] = child.x
pos[int(child.tree.node), 1] = child.y
new_roots.extend(children)
old_roots = new_roots
pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1]
return pos
def radial_layout(edge_list, root_index=0):
root, num_nodes = tree_from_edge_list(edge_list, root_index)
dt = buchheim(root)
pos = np.zeros((num_nodes, 2))
pos[int(dt.tree.node), 0] = dt.x
pos[int(dt.tree.node), 1] = dt.y
old_roots = [dt]
new_roots = []
while(len(old_roots) > 0):
new_roots = []
for temp_root in old_roots:
children = temp_root.children
for child in children:
pos[int(child.tree.node), 0] = child.x
pos[int(child.tree.node), 1] = child.y
new_roots.extend(children)
old_roots = new_roots
# pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1]
pos[:, 0] = pos[:, 0] - np.min(pos[:, 0])
pos[:, 1] = pos[:, 1] - np.min(pos[:, 1])
pos[:, 0] = pos[:, 0] / np.max(pos[:, 0])
pos[:, 0] = pos[:, 0] - pos[:, 0][root_index]
range_ = np.max(pos[:, 0]) - np.min(pos[:, 0])
pos[:, 0] = pos[:, 0] / range_
pos[:, 0] = pos[:, 0] * np.pi * 1.98
pos[:, 1] = pos[:, 1] / np.max(pos[:, 1])
new_pos = np.zeros((num_nodes, 2))
new_pos[:, 0] = pos[:, 1] * np.cos(pos[:, 0])
new_pos[:, 1] = pos[:, 1] * np.sin(pos[:, 0])
return new_pos
def graph_layout(edge_list, layout='tree', root_index=0, k=None, seed=None, iterations=500):
if layout == 'tree':
return tree_layout(edge_list, root_index=root_index)
elif layout == 'spring':
return spring_layout(edge_list, k=k, seed=seed, iterations=iterations)
elif layout == 'radial':
return radial_layout(edge_list, root_index=root_index)
else:
raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']")
def vertex_max_degree(graph, vertices):
degrees = [Graph.VertexDegree(graph, vertex) for vertex in vertices]
i = degrees.index(max(degrees))
return vertices[i], i
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Flatten - Error: The input graph is not a valid topologic graph. Returning None.")
return None
d = Graph.MeshData(graph)
vertices = d['vertices']
edges = d['edges']
v_dicts = d['vertexDictionaries']
e_dicts = d['edgeDictionaries']
vertices = Graph.Vertices(graph)
if rootVertex == None:
rootVertex, root_index = vertex_max_degree(graph, vertices)
else:
root_index = Vertex.Index(rootVertex, vertices)
if 'rad' in layout.lower():
positions = radial_layout(edges, root_index=root_index)
elif 'spring' in layout.lower():
positions = spring_layout(edges, k=k, seed=seed, iterations=iterations)
elif 'tree' in layout.lower():
positions = tree_layout(edges, root_index=root_index)
else:
raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']")
positions = positions.tolist()
positions = [[p[0], p[1], 0] for p in positions]
flat_graph = Graph.ByMeshData(positions, edges, v_dicts, e_dicts, tolerance=tolerance)
return flat_graph
@staticmethod
def GlobalClusteringCoefficient(graph):
"""
Returns the global clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
int
The computed global clustering coefficient.
"""
from topologicpy.Topology import Topology
def global_clustering_coefficient(adjacency_matrix):
total_triangles = 0
total_possible_triangles = 0
num_nodes = len(adjacency_matrix)
for i in range(num_nodes):
neighbors = [j for j, value in enumerate(adjacency_matrix[i]) if value == 1]
num_neighbors = len(neighbors)
num_triangles = 0
if num_neighbors >= 2:
# Count the number of connections between the neighbors
for i in range(num_neighbors):
for j in range(i + 1, num_neighbors):
if adjacency_matrix[neighbors[i]][neighbors[j]] == 1:
num_triangles += 1
# Update total triangles and possible triangles
total_triangles += num_triangles
total_possible_triangles += num_neighbors * (num_neighbors - 1) // 2
# Calculate the global clustering coefficient
global_clustering_coeff = 3.0 * total_triangles / total_possible_triangles if total_possible_triangles > 0 else 0.0
return global_clustering_coeff
if not Topology.IsInstance(graph, "Graph"):
print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.")
return None
adjacency_matrix = Graph.AdjacencyMatrix(graph)
return global_clustering_coefficient(adjacency_matrix)
@staticmethod
def Guid(graph):
"""
Returns the guid of the input graph
Parameters
----------
graph : topologic_core.Graph
The input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Guid - Error: the input graph parameter is not a valid graph. Returning None.")
return None
return graph.GetGUID()
@staticmethod
def IncomingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list:
"""
Returns the incoming edges connected to a vertex. An edge is considered incoming if its end vertex is
coincident with the input vertex.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
directed : bool , optional
If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of incoming edges
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IncomingEdges - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.IncomingEdges - Error: The input vertex parameter is not a valid vertex. Returning None.")
return None
edges = Graph.Edges(graph, [vertex])
if directed == False:
return edges
incoming_edges = []
for edge in edges:
ev = Edge.EndVertex(edge)
if Vertex.Distance(vertex, ev) < tolerance:
incoming_edges.append(edge)
return incoming_edges
@staticmethod
def IncomingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list:
"""
Returns the incoming vertices connected to a vertex. A vertex is considered incoming if it is an adjacent vertex to the input vertex
and the the edge connecting it to the input vertex is an incoming edge.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
directed : bool , optional
If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of incoming vertices
"""
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IncomingVertices - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.IncomingVertices - Error: The input vertex parameter is not a valid vertex. Returning None.")
return None
if directed == False:
return Graph.AdjacentVertices(graph, vertex)
incoming_edges = Graph.IncomingEdges(graph, vertex, directed=directed, tolerance=tolerance)
incoming_vertices = []
for edge in incoming_edges:
sv = Edge.StartVertex(edge)
incoming_vertices.append(Graph.NearestVertex(graph, sv))
return incoming_vertices
@staticmethod
def IsBipartite(graph, tolerance=0.0001):
"""
Returns True if the input graph is bipartite. Returns False otherwise. See https://en.wikipedia.org/wiki/Bipartite_graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
bool
True if the input graph is complete. False otherwise
"""
# From https://www.geeksforgeeks.org/bipartite-graph/
# This code is contributed by divyesh072019.
from topologicpy.Topology import Topology
def isBipartite(V, adj):
# vector to store colour of vertex
# assigning all to -1 i.e. uncoloured
# colours are either 0 or 1
# for understanding take 0 as red and 1 as blue
col = [-1]*(V)
# queue for BFS storing {vertex , colour}
q = []
#loop incase graph is not connected
for i in range(V):
# if not coloured
if (col[i] == -1):
# colouring with 0 i.e. red
q.append([i, 0])
col[i] = 0
while len(q) != 0:
p = q[0]
q.pop(0)
# current vertex
v = p[0]
# colour of current vertex
c = p[1]
# traversing vertexes connected to current vertex
for j in adj[v]:
# if already coloured with parent vertex color
# then bipartite graph is not possible
if (col[j] == c):
return False
# if uncoloured
if (col[j] == -1):
# colouring with opposite color to that of parent
if c == 1:
col[j] = 0
else:
col[j] = 1
q.append([j, col[j]])
# if all vertexes are coloured such that
# no two connected vertex have same colours
return True
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IsBipartite - Error: The input graph is not a valid graph. Returning None.")
return None
order = Graph.Order(graph)
adjList = Graph.AdjacencyList(graph, tolerance=tolerance)
return isBipartite(order, adjList)
@staticmethod
def IsComplete(graph):
"""
Returns True if the input graph is complete. Returns False otherwise. See https://en.wikipedia.org/wiki/Complete_graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
bool
True if the input graph is complete. False otherwise
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IsComplete - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.IsComplete()
@staticmethod
def IsErdoesGallai(graph, sequence):
"""
Returns True if the input sequence satisfies the Erdős–Gallai theorem. Returns False otherwise. See https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Gallai_theorem.
Parameters
----------
graph : topologic_core.Graph
The input graph.
sequence : list
The input sequence.
Returns
-------
bool
True if the input sequence satisfies the Erdős–Gallai theorem. False otherwise.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IsErdoesGallai - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.IsErdoesGallai(sequence)
@staticmethod
def IsolatedVertices(graph):
"""
Returns the list of isolated vertices in the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
list
The list of isolated vertices.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IsolatedVertices - Error: The input graph is not a valid graph. Returning None.")
return None
vertices = []
_ = graph.IsolatedVertices(vertices)
return vertices
@staticmethod
def JSONData(graph,
verticesKey="vertices",
edgesKey="edges",
vertexLabelKey="",
edgeLabelKey="",
sourceKey="source",
targetKey="target",
xKey="x",
yKey="y",
zKey="z",
geometryKey="brep",
mantissa=6):
"""
Converts the input graph into JSON data.
Parameters
----------
graph : topologic_core.Graph
The input graph.
verticesKey : str , optional
The desired key name to call vertices. The default is "vertices".
edgesKey : str , optional
The desired key name to call edges. The default is "edges".
vertexLabelKey : str , optional
If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number.
Note: If vertex labels are not unique, they will be forced to be unique.
edgeLabelKey : str , optional
If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number.
Note: If edge labels are not unique, they will be forced to be unique.
sourceKey : str , optional
The dictionary key used to store the source vertex. The default is "source".
targetKey : str , optional
The dictionary key used to store the target vertex. The default is "source".
xKey : str , optional
The desired key name to use for x-coordinates. The default is "x".
yKey : str , optional
The desired key name to use for y-coordinates. The default is "y".
zKey : str , optional
The desired key name to use for z-coordinates. The default is "z".
geometryKey : str , optional
The desired key name to use for geometry. The default is "brep".
mantissa : int , optional
The desired length of the mantissa. The default is 6.
Returns
-------
dict
The JSON data
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
from topologicpy.Helper import Helper
vertices = Graph.Vertices(graph)
j_data = {}
j_data[verticesKey] = {}
j_data[edgesKey] = {}
n = max(len(str(len(vertices))), 4)
v_labels = []
v_dicts = []
for i, v in enumerate(vertices):
d = Topology.Dictionary(v)
d = Dictionary.SetValueAtKey(d, xKey, Vertex.X(v, mantissa=mantissa))
d = Dictionary.SetValueAtKey(d, yKey, Vertex.Y(v, mantissa=mantissa))
d = Dictionary.SetValueAtKey(d, zKey, Vertex.Z(v, mantissa=mantissa))
if geometryKey:
v_d = Topology.Dictionary(v)
brep = Dictionary.ValueAtKey(v_d,"brep")
if brep:
d = Dictionary.SetValueAtKey(d, geometryKey, brep)
v_dict = Dictionary.PythonDictionary(d)
v_label = Dictionary.ValueAtKey(d, vertexLabelKey)
if isinstance(v_label, str):
v_label = Dictionary.ValueAtKey(d, vertexLabelKey)
else:
v_label = "Vertex_"+str(i).zfill(n)
v_labels.append(v_label)
v_dicts.append(v_dict)
v_labels = Helper.MakeUnique(v_labels)
for i, v_label in enumerate(v_labels):
j_data[verticesKey][v_label] = v_dicts[i]
edges = Graph.Edges(graph)
n = len(str(len(edges)))
e_labels = []
e_dicts = []
for i, e in enumerate(edges):
sv = Edge.StartVertex(e)
ev = Edge.EndVertex(e)
svi = Vertex.Index(sv, vertices)
evi = Vertex.Index(ev, vertices)
sv_label = v_labels[svi]
ev_label = v_labels[evi]
d = Topology.Dictionary(e)
d = Dictionary.SetValueAtKey(d, sourceKey, sv_label)
d = Dictionary.SetValueAtKey(d, targetKey, ev_label)
e_dict = Dictionary.PythonDictionary(d)
e_label = Dictionary.ValueAtKey(d, edgeLabelKey)
if isinstance(e_label, str):
e_label = Dictionary.ValueAtKey(d, edgeLabelKey)
else:
e_label = "Edge_"+str(i).zfill(n)
e_labels.append(e_label)
e_dicts.append(e_dict)
e_labels = Helper.MakeUnique(e_labels)
for i, e_label in enumerate(e_labels):
j_data[edgesKey][e_label] = e_dicts[i]
return j_data
@staticmethod
def JSONString(graph,
verticesKey="vertices",
edgesKey="edges",
vertexLabelKey="",
edgeLabelKey="",
xKey = "x",
yKey = "y",
zKey = "z",
indent=4,
sortKeys=False,
mantissa=6):
"""
Converts the input graph into JSON data.
Parameters
----------
graph : topologic_core.Graph
The input graph.
verticesKey : str , optional
The desired key name to call vertices. The default is "vertices".
edgesKey : str , optional
The desired key name to call edges. The default is "edges".
vertexLabelKey : str , optional
If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number.
Note: If vertex labels are not unique, they will be forced to be unique.
edgeLabelKey : str , optional
If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number.
Note: If edge labels are not unique, they will be forced to be unique.
xKey : str , optional
The desired key name to use for x-coordinates. The default is "x".
yKey : str , optional
The desired key name to use for y-coordinates. The default is "y".
zKey : str , optional
The desired key name to use for z-coordinates. The default is "z".
indent : int , optional
The desired amount of indent spaces to use. The default is 4.
sortKeys : bool , optional
If set to True, the keys will be sorted. Otherwise, they won't be. The default is False.
mantissa : int , optional
The desired length of the mantissa. The default is 6.
Returns
-------
str
The JSON str
"""
import json
json_data = Graph.JSONData(graph, verticesKey=verticesKey, edgesKey=edgesKey, vertexLabelKey=vertexLabelKey, edgeLabelKey=edgeLabelKey, xKey=xKey, yKey=yKey, zKey=zKey, mantissa=mantissa)
json_string = json.dumps(json_data, indent=indent, sort_keys=sortKeys)
return json_string
@staticmethod
def LocalClusteringCoefficient(graph, vertices=None, mantissa=6):
"""
Returns the local clustering coefficient of the input list of vertices within the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertices : list , optional
The input list of vertices. If set to None, the local clustering coefficient of all vertices will be computed.
mantissa : int , optional
The desired length of the mantissa. The default is 6.
Returns
-------
list
The list of local clustering coefficient. The order of the list matches the order of the list of input vertices.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Topology import Topology
def local_clustering_coefficient(adjacency_matrix, node):
"""
Compute the local clustering coefficient for a given node in a graph represented by an adjacency matrix.
Parameters:
- adjacency_matrix: 2D list representing the adjacency matrix of the graph
- node: Node for which the local clustering coefficient is computed
Returns:
- Local clustering coefficient for the given node
"""
neighbors = [i for i, value in enumerate(adjacency_matrix[node]) if value == 1]
num_neighbors = len(neighbors)
if num_neighbors < 2:
# If the node has less than 2 neighbors, the clustering coefficient is undefined
return 0.0
# Count the number of connections between the neighbors
num_connections = 0
for i in range(num_neighbors):
for j in range(i + 1, num_neighbors):
if adjacency_matrix[neighbors[i]][neighbors[j]] == 1:
num_connections += 1
# Calculate the local clustering coefficient
local_clustering_coeff = 2.0 * num_connections / (num_neighbors * (num_neighbors - 1))
return local_clustering_coeff
if not Topology.IsInstance(graph, "Graph"):
print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if vertices == None:
vertices = Graph.Vertices(graph)
if Topology.IsInstance(vertices, "Vertex"):
vertices = [vertices]
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
if len(vertices) < 1:
print("Graph.LocalClusteringCoefficient - Error: The input vertices parameter does not contain valid vertices. Returning None.")
return None
g_vertices = Graph.Vertices(graph)
adjacency_matrix = Graph.AdjacencyMatrix(graph)
lcc = []
for v in vertices:
i = Vertex.Index(v, g_vertices)
if not i == None:
lcc.append(round(local_clustering_coefficient(adjacency_matrix, i), mantissa))
else:
lcc.append(None)
return lcc
@staticmethod
def LongestPath(graph, vertexA, vertexB, vertexKey=None, edgeKey=None, costKey=None, timeLimit=10, tolerance=0.0001):
"""
Returns the longest path that connects the input vertices.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
vertexKey : str , optional
The vertex key to maximize. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value must be numeric. The default is None.
edgeKey : str , optional
The edge key to maximize. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length".
costKey : str , optional
If not None, the total cost of the longest_path will be stored in its dictionary under this key. The default is None.
timeLimit : int , optional
The time limit in second. The default is 10 seconds.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Wire
The longest path between the input vertices.
"""
from topologicpy. Dictionary import Dictionary
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Wire import Wire
from topologicpy.Cluster import Cluster
from topologicpy.Topology import Topology
from topologicpy.Helper import Helper
if not Topology.IsInstance(graph, "Graph"):
print("Graph.LongestPath - Error: the input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.LongestPath - Error: the input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.LongestPath - Error: the input vertexB is not a valid vertex. Returning None.")
return None
g_edges = Graph.Edges(graph)
paths = Graph.AllPaths(graph, vertexA, vertexB, timeLimit=timeLimit)
if not paths:
print("Graph.LongestPath - Error: Could not find any paths within the specified time limit. Returning None.")
return None
if len(paths) < 1:
print("Graph.LongestPath - Error: Could not find any paths within the specified time limit. Returning None.")
return None
if edgeKey == None:
lengths = [len(Topology.Edges(path)) for path in paths]
elif edgeKey.lower() == "length":
lengths = [Wire.Length(path) for path in paths]
else:
lengths = []
for path in paths:
edges = Topology.Edges(path)
pathCost = 0
for edge in edges:
index = Edge.Index(edge, g_edges)
d = Topology.Dictionary(g_edges[index])
value = Dictionary.ValueAtKey(d, edgeKey)
if not value == None:
pathCost += value
lengths.append(pathCost)
if not vertexKey == None:
g_vertices = Graph.Vertices(graph)
for i, path in enumerate(paths):
vertices = Topology.Vertices(path)
pathCost = 0
for vertex in vertices:
index = Vertex.Index(vertex, g_vertices)
d = Topology.Dictionary(g_vertices[index])
value = Dictionary.ValueAtKey(d, vertexKey)
if not value == None:
pathCost += value
lengths[i] += pathCost
new_paths = Helper.Sort(paths, lengths)
temp_path = new_paths[-1]
cost = lengths[-1]
new_edges = []
for edge in Topology.Edges(temp_path):
new_edges.append(g_edges[Edge.Index(edge, g_edges)])
longest_path = Topology.SelfMerge(Cluster.ByTopologies(new_edges), tolerance=tolerance)
sv = Topology.Vertices(longest_path)[0]
if Vertex.Distance(sv, vertexB) < tolerance: # Wire is reversed. Re-reverse it
if Topology.IsInstance(longest_path, "Edge"):
longest_path = Edge.Reverse(longest_path)
elif Topology.IsInstance(longest_path, "Wire"):
longest_path = Wire.Reverse(longest_path)
longest_path = Wire.OrientEdges(longest_path, Wire.StartVertex(longest_path), tolerance=tolerance)
if not costKey == None:
lengths.sort()
d = Dictionary.ByKeysValues([costKey], [cost])
longest_path = Topology.SetDictionary(longest_path, d)
return longest_path
@staticmethod
def MaximumDelta(graph):
"""
Returns the maximum delta of the input graph. The maximum delta of a graph is the maximum degree of a vertex in the graph.
Parameters
----------
graph : topologic_core.Graph
the input graph.
Returns
-------
int
The maximum delta.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.MaximumDelta - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.MaximumDelta()
@staticmethod
def MaximumFlow(graph, source, sink, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=False, residualKey="residual", tolerance=0.0001):
"""
Returns the maximum flow of the input graph. See https://en.wikipedia.org/wiki/Maximum_flow_problem
Parameters
----------
graph : topologic_core.Graph
The input graph. This is assumed to be a directed graph
source : topologic_core.Vertex
The input source vertex.
sink : topologic_core.Vertex
The input sink/target vertex.
edgeKeyFwd : str , optional
The edge dictionary key to use to find the value of the forward capacity of the edge. If not set, the length of the edge is used as its capacity. The default is None.
edgeKeyBwd : str , optional
The edge dictionary key to use to find the value of the backward capacity of the edge. This is only considered if the edge is set to be bidrectional. The default is None.
bidirKey : str , optional
The edge dictionary key to use to determine if the edge is bidrectional. The default is None.
bidrectional : bool , optional
If set to True, the whole graph is considered to be bidirectional. The default is False.
residualKey : str , optional
The name of the key to use to store the residual value of each edge capacity in the input graph. The default is "residual".
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
float
The maximum flow.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
# Using BFS as a searching algorithm
def searching_algo_BFS(adjMatrix, s, t, parent):
visited = [False] * (len(adjMatrix))
queue = []
queue.append(s)
visited[s] = True
while queue:
u = queue.pop(0)
for ind, val in enumerate(adjMatrix[u]):
if visited[ind] == False and val > 0:
queue.append(ind)
visited[ind] = True
parent[ind] = u
return True if visited[t] else False
# Applying fordfulkerson algorithm
def ford_fulkerson(adjMatrix, source, sink):
am = adjMatrix.copy()
row = len(am)
parent = [-1] * (row)
max_flow = 0
while searching_algo_BFS(am, source, sink, parent):
path_flow = float("Inf")
s = sink
while(s != source):
path_flow = min(path_flow, am[parent[s]][s])
s = parent[s]
# Adding the path flows
max_flow += path_flow
# Updating the residual values of edges
v = sink
while(v != source):
u = parent[v]
am[u][v] -= path_flow
am[v][u] += path_flow
v = parent[v]
return [max_flow, am]
if edgeKeyFwd == None:
useEdgeLength = True
else:
useEdgeLength = False
adjMatrix = Graph.AdjacencyMatrix(graph, edgeKeyFwd=edgeKeyFwd, edgeKeyBwd=edgeKeyBwd, bidirKey=bidirKey, bidirectional=bidirectional, useEdgeIndex = False, useEdgeLength=useEdgeLength, tolerance=tolerance)
edgeMatrix = Graph.AdjacencyMatrix(graph, edgeKeyFwd=edgeKeyFwd, edgeKeyBwd=edgeKeyBwd, bidirKey=bidirKey, bidirectional=bidirectional, useEdgeIndex = True, useEdgeLength=False, tolerance=tolerance)
vertices = Graph.Vertices(graph)
edges = Graph.Edges(graph)
sourceIndex = Vertex.Index(source, vertices)
sinkIndex = Vertex.Index(sink, vertices)
max_flow, am = ford_fulkerson(adjMatrix=adjMatrix, source=sourceIndex, sink=sinkIndex)
for i in range(len(am)):
row = am[i]
for j in range(len(row)):
residual = am[i][j]
edge = edges[edgeMatrix[i][j]-1]
d = Topology.Dictionary(edge)
if not d == None:
keys = Dictionary.Keys(d)
values = Dictionary.Values(d)
else:
keys = []
values = []
keys.append(residualKey)
values.append(residual)
d = Dictionary.ByKeysValues(keys, values)
edge = Topology.SetDictionary(edge, d)
return max_flow
@staticmethod
def MeshData(g):
"""
Returns the mesh data of the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
dict
The python dictionary of the mesh data of the input graph. The keys in the dictionary are:
'vertices' : The list of [x, y, z] coordinates of the vertices.
'edges' : the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j].
'vertexDictionaries' : The python dictionaries of the vertices (in the same order as the list of vertices).
'edgeDictionaries' : The python dictionaries of the edges (in the same order as the list of edges).
"""
from topologicpy.Vertex import Vertex
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
g_vertices = Graph.Vertices(g)
m_vertices = []
v_dicts = []
for g_vertex in g_vertices:
m_vertices.append(Vertex.Coordinates(g_vertex))
d = Dictionary.PythonDictionary(Topology.Dictionary(g_vertex))
v_dicts.append(d)
g_edges = Graph.Edges(g)
m_edges = []
e_dicts = []
for g_edge in g_edges:
sv = g_edge.StartVertex()
ev = g_edge.EndVertex()
si = Vertex.Index(sv, g_vertices)
ei = Vertex.Index(ev, g_vertices)
m_edges.append([si, ei])
d = Dictionary.PythonDictionary(Topology.Dictionary(g_edge))
e_dicts.append(d)
return {'vertices':m_vertices,
'edges': m_edges,
'vertexDictionaries': v_dicts,
'edgeDictionaries': e_dicts
}
@staticmethod
def MinimumDelta(graph):
"""
Returns the minimum delta of the input graph. The minimum delta of a graph is the minimum degree of a vertex in the graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
int
The minimum delta.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.MinimumDelta - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.MinimumDelta()
@staticmethod
def MinimumSpanningTree(graph, edgeKey=None, tolerance=0.0001):
"""
Returns the minimum spanning tree of the input graph. See https://en.wikipedia.org/wiki/Minimum_spanning_tree.
Parameters
----------
graph : topologic_core.Graph
The input graph.
edgeKey : string , optional
If set, the value of the edgeKey will be used as the weight and the tree will minimize the weight. The value associated with the edgeKey must be numerical. If the key is not set, the edges will be sorted by their length. The default is None
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The minimum spanning tree.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
def vertexInList(vertex, vertexList, tolerance=0.0001):
for v in vertexList:
if Vertex.Distance(v, vertex) < tolerance:
return True
return False
if not Topology.IsInstance(graph, "Graph"):
print("Graph.MinimumSpanningTree - Error: The input graph is not a valid graph. Returning None.")
return None
edges = Graph.Edges(graph)
vertices = Graph.Vertices(graph)
values = []
if isinstance(edgeKey, str):
for edge in edges:
d = Dictionary.Dictionary(edge)
value = Dictionary.ValueAtKey(d, edgeKey)
if value == None or not isinstance(value, int) or not isinstance(value, float):
return None
values.append(value)
else:
for edge in edges:
value = Edge.Length(edge)
values.append(value)
keydict = dict(zip(edges, values))
edges.sort(key=keydict.get)
mst = Graph.ByVerticesEdges(vertices,[])
for edge in edges:
sv = Edge.StartVertex(edge)
ev = Edge.EndVertex(edge)
if len(Graph.Vertices(mst)) > 0:
if not Graph.Path(mst, Graph.NearestVertex(mst, sv), Graph.NearestVertex(mst, ev)):
d = Topology.Dictionary(edge)
if len(Dictionary.Keys(d)) > 0:
tranEdgeDicts = True
else:
tranEdgeDicts = False
mst = Graph.AddEdge(mst, edge, transferVertexDictionaries=False, transferEdgeDictionaries=tranEdgeDicts)
return mst
@staticmethod
def NavigationGraph(face, sources=None, destinations=None, tolerance=0.0001, progressBar=True):
"""
Creates a 2D navigation graph.
Parameters
----------
face : topologic_core.Face
The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles
sources : list
The first input list of sources (vertices). Navigation edges will connect these veritces to destinations.
destinations : list
The input list of destinations (vertices). Navigation edges will connect these vertices to sources.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
tqdm : bool , optional
If set to True, a tqdm progress bar is shown. Otherwise, it is not. The default is True.
Returns
-------
topologic_core.Graph
The navigation graph.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Wire import Wire
from topologicpy.Face import Face
from topologicpy.Graph import Graph
from topologicpy.Cluster import Cluster
from topologicpy.Topology import Topology
import topologic_core as topologic
from tqdm.auto import tqdm
if not Topology.IsInstance(face, "Face"):
print("Graph.NavigationGraph - Error: The input face parameter is not a valid face. Returning None")
return None
if sources == None:
sources = Topology.Vertices(face)
if destinations == None:
destinations = Topology.Vertices(face)
if not isinstance(sources, list):
print("Graph.NavigationGraph - Error: The input sources parameter is not a valid list. Returning None")
return None
if not isinstance(destinations, list):
print("Graph.NavigationGraph - Error: The input destinations parameter is not a valid list. Returning None")
return None
sources = [v for v in sources if Topology.IsInstance(v, "Vertex")]
if len(sources) < 1:
print("Graph.NavigationGraph - Error: The input sources parameter does not contain any vertices. Returning None")
return None
destinations = [v for v in destinations if Topology.IsInstance(v, "Vertex")]
#if len(destinations) < 1: #Nothing to navigate to, so return a graph made of sources
#return Graph.ByVerticesEdges(sources, [])
# Add obstuse angles of external boundary to viewpoints
e_boundary = Face.ExternalBoundary(face)
if Topology.IsInstance(e_boundary, "Wire"):
vertices = Topology.Vertices(e_boundary)
interior_angles = Wire.InteriorAngles(e_boundary)
for i, ang in enumerate(interior_angles):
if ang > 180:
sources.append(vertices[i])
destinations.append(vertices[i])
i_boundaries = Face.InternalBoundaries(face)
for i_boundary in i_boundaries:
if Topology.IsInstance(i_boundary, "Wire"):
vertices = Topology.Vertices(i_boundary)
interior_angles = Wire.InteriorAngles(i_boundary)
for i, ang in enumerate(interior_angles):
if ang < 180:
sources.append(vertices[i])
destinations.append(vertices[i])
used = []
for i in range(max(len(sources), len(destinations))):
temp_row = []
for j in range(max(len(sources), len(destinations))):
temp_row.append(0)
used.append(temp_row)
final_edges = []
if progressBar:
the_range = tqdm(range(len(sources)))
else:
the_range = range(len(sources))
for i in the_range:
source = sources[i]
index_b = Vertex.Index(source, destinations)
for j in range(len(destinations)):
destination = destinations[j]
index_a = Vertex.Index(destination, sources)
if used[i][j] == 1 or used[j][i]:
continue
if Vertex.Distance(source, destination) > tolerance:
edge = Edge.ByVertices([source,destination])
e = Topology.Boolean(edge, face, operation="intersect")
if Topology.IsInstance(e, "Edge"):
final_edges.append(edge)
used[i][j] = 1
if not index_a == None and not index_b == None:
used[j][i] = 1
if len(i_boundaries) > 0:
holes_edges = Topology.Edges(Cluster.ByTopologies(i_boundaries))
final_edges += holes_edges
if len(final_edges) > 0:
final_vertices = Topology.Vertices(Cluster.ByTopologies(final_edges))
g = Graph.ByVerticesEdges(final_vertices, final_edges)
return g
return None
@staticmethod
def NearestVertex(graph, vertex):
"""
Returns the vertex in the input graph that is the nearest to the input vertex.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
Returns
-------
topologic_core.Vertex
The vertex in the input graph that is the nearest to the input vertex.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.NearestVertex - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.NearestVertex - Error: The input vertex is not a valid vertex. Returning None.")
return None
vertices = Graph.Vertices(graph)
nearestVertex = vertices[0]
nearestDistance = Vertex.Distance(vertex, nearestVertex)
for aGraphVertex in vertices:
newDistance = Vertex.Distance(vertex, aGraphVertex)
if newDistance < nearestDistance:
nearestDistance = newDistance
nearestVertex = aGraphVertex
return nearestVertex
@staticmethod
def NetworkXGraph(graph, tolerance=0.0001):
"""
converts the input graph into a NetworkX Graph. See http://networkx.org
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
networkX Graph
The created networkX Graph
"""
from topologicpy.Vertex import Vertex
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
try:
import networkx as nx
except:
print("Graph.NetworkXGraph - Information: Installing required networkx library.")
try:
os.system("pip install networkx")
except:
os.system("pip install networkx --user")
try:
import networkx as nx
print("Graph.NetworkXGraph - Infromation: networkx library installed correctly.")
except:
warnings.warn("Graph - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
return None
if not Topology.IsInstance(graph, "Graph"):
print("Graph.NetworkXGraph - Error: The input graph is not a valid graph. Returning None.")
return None
nxGraph = nx.Graph()
vertices = Graph.Vertices(graph)
order = len(vertices)
nodes = []
for i in range(order):
v = vertices[i]
d = Topology.Dictionary(vertices[i])
if d:
keys = Dictionary.Keys(d)
if not keys:
keys = []
values = Dictionary.Values(d)
if not values:
values = []
keys += ["x","y","z"]
import random
values += [Vertex.X(v), Vertex.Y(v), Vertex.Z(v)]
d = Dictionary.ByKeysValues(keys,values)
pythonD = Dictionary.PythonDictionary(d)
nodes.append((i, pythonD))
else:
nodes.append((i, {"name": str(i)}))
nxGraph.add_nodes_from(nodes)
for i in range(order):
v = vertices[i]
adjVertices = Graph.AdjacentVertices(graph, vertices[i])
for adjVertex in adjVertices:
adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance)
if not adjIndex == None:
nxGraph.add_edge(i,adjIndex, length=(Vertex.Distance(v, adjVertex)))
pos=nx.spring_layout(nxGraph, k=0.2)
nx.set_node_attributes(nxGraph, pos, "pos")
return nxGraph
@staticmethod
def Order(graph):
"""
Returns the graph order of the input graph. The graph order is its number of vertices.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
int
The number of vertices in the input graph
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Order - Error: The input graph is not a valid graph. Returning None.")
return None
return len(Graph.Vertices(graph))
@staticmethod
def OutgoingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list:
"""
Returns the outgoing edges connected to a vertex. An edge is considered outgoing if its start vertex is
coincident with the input vertex.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
directed : bool , optional
If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of outgoing edges
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.IncomingEdges - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.IncomingEdges - Error: The input vertex parameter is not a valid vertex. Returning None.")
return None
edges = Graph.Edges(graph, [vertex])
if directed == False:
return edges
outgoing_edges = []
for edge in edges:
sv = Edge.StartVertex(edge)
if Vertex.Distance(vertex, sv) < tolerance:
outgoing_edges.append(edge)
return outgoing_edges
@staticmethod
def OutgoingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list:
"""
Returns the list of outgoing vertices connected to a vertex. A vertex is considered outgoing if it is an adjacent vertex to the input vertex
and the the edge connecting it to the input vertex is an outgoing edge.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
directed : bool , optional
If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of incoming vertices
"""
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.OutgoingVertices - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.OutgoingVertices - Error: The input vertex parameter is not a valid vertex. Returning None.")
return None
if directed == False:
return Graph.AdjacentVertices(graph, vertex)
outgoing_edges = Graph.OutgoingEdges(graph, vertex, directed=directed, tolerance=tolerance)
outgoing_vertices = []
for edge in outgoing_edges:
ev = Edge.EndVertex(edge)
outgoing_vertices.append(Graph.NearestVertex(graph, ev))
return outgoing_vertices
@staticmethod
def PageRank(graph, alpha=0.85, maxIterations=100, normalize=True, directed=False, mantissa=6, tolerance=0.0001):
"""
Calculates PageRank scores for nodes in a directed graph. see https://en.wikipedia.org/wiki/PageRank.
Parameters
----------
graph : topologic_core.Graph
The input graph.
alpha : float , optional
The damping (dampening) factor. The default is 0.85. See https://en.wikipedia.org/wiki/PageRank.
maxIterations : int , optional
The maximum number of iterations to calculate the page rank. The default is 100.
normalize : bool , optional
If set to True, the results will be normalized from 0 to 1. Otherwise, they won't be. The default is True.
directed : bool , optional
If set to True, the graph is considered as a directed graph. Otherwise, it will be considered as an undirected graph. The default is False.
mantissa : int , optional
The desired length of the mantissa.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of page ranks for the vertices in the graph.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Helper import Helper
vertices = Graph.Vertices(graph)
num_vertices = len(vertices)
if num_vertices < 1:
print("Graph.PageRank - Error: The input graph parameter has no vertices. Returning None")
return None
initial_score = 1.0 / num_vertices
scores = [initial_score for vertex in vertices]
for _ in range(maxIterations):
new_scores = [0 for vertex in vertices]
for i, vertex in enumerate(vertices):
incoming_score = 0
for incoming_vertex in Graph.IncomingVertices(graph, vertex, directed=directed):
if len(Graph.IncomingVertices(graph, incoming_vertex, directed=directed)) > 0:
incoming_score += scores[Vertex.Index(incoming_vertex, vertices)] / len(Graph.IncomingVertices(graph, incoming_vertex, directed=directed))
new_scores[i] = alpha * incoming_score + (1 - alpha) / num_vertices
# Check for convergence
if all(abs(new_scores[i] - scores[i]) < tolerance for i in range(len(vertices))):
break
scores = new_scores
if normalize == True:
scores = Helper.Normalize(scores, mantissa=mantissa)
else:
scores = [round(x, mantissa) for x in scores]
return scores
@staticmethod
def Path(graph, vertexA, vertexB, tolerance=0.0001):
"""
Returns a path (wire) in the input graph that connects the input vertices.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
tolerance : float, optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Wire
The path (wire) in the input graph that connects the input vertices.
"""
from topologicpy.Wire import Wire
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Path - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.Path - Error: The input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.Path - Error: The input vertexB is not a valid vertex. Returning None.")
return None
path = graph.Path(vertexA, vertexB)
if Topology.IsInstance(path, "Wire"):
path = Wire.OrientEdges(path, Wire.StartVertex(path), tolerance=tolerance)
return path
@staticmethod
def PyvisGraph(graph, path, overwrite=True, height=900, backgroundColor="white", fontColor="black", notebook=False,
vertexSize=6, vertexSizeKey=None, vertexColor="black", vertexColorKey=None, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=None, minVertexGroup=None, maxVertexGroup=None,
edgeLabelKey=None, edgeWeight=0, edgeWeightKey=None, showNeighbours=True, selectMenu=True, filterMenu=True, colorScale="viridis"):
"""
Displays a pyvis graph. See https://pyvis.readthedocs.io/.
Parameters
----------
graph : topologic_core.Graph
The input graph.
path : str
The desired file path to the HTML file into which to save the pyvis graph.
overwrite : bool , optional
If set to True, the HTML file is overwritten.
height : int , optional
The desired figure height in pixels. The default is 900 pixels.
backgroundColor : str, optional
The desired background color for the figure. This can be a named color or a hexadecimal value. The default is 'white'.
fontColor : str , optional
The desired font color for the figure. This can be a named color or a hexadecimal value. The default is 'black'.
notebook : bool , optional
If set to True, the figure will be targeted at a Jupyter Notebook. Note that this is not working well. Pyvis has bugs. The default is False.
vertexSize : int , optional
The desired default vertex size. The default is 6.
vertexSizeKey : str , optional
If not set to None, the vertex size will be derived from the dictionary value set at this key. If set to "degree", the size of the vertex will be determined by its degree (number of neighbors). The default is None.
vertexColor : int , optional
The desired default vertex color. his can be a named color or a hexadecimal value. The default is 'black'.
vertexColorKey : str , optional
If not set to None, the vertex color will be derived from the dictionary value set at this key. The default is None.
vertexLabelKey : str , optional
If not set to None, the vertex label will be derived from the dictionary value set at this key. The default is None.
vertexGroupKey : str , optional
If not set to None, the vertex color will be determined by the group the vertex belongs to as derived from the value set at this key. The default is None.
vertexGroups : list , optional
The list of all possible vertex groups. This will help in vertex coloring. The default is None.
minVertexGroup : int or float , optional
If the vertex groups are numeric, specify the minimum value you wish to consider for vertex coloring. The default is None.
maxVertexGroup : int or float , optional
If the vertex groups are numeric, specify the maximum value you wish to consider for vertex coloring. The default is None.
edgeWeight : int , optional
The desired default weight of the edge. This determines its thickness. The default is 0.
edgeWeightKey : str, optional
If not set to None, the edge weight will be derived from the dictionary value set at this key. If set to "length" or "distance", the weight of the edge will be determined by its geometric length. The default is None.
edgeLabelKey : str , optional
If not set to None, the edge label will be derived from the dictionary value set at this key. The default is None.
showNeighbors : bool , optional
If set to True, a list of neighbors is shown when you hover over a vertex. The default is True.
selectMenu : bool , optional
If set to True, a selection menu will be displayed. The default is True
filterMenu : bool , optional
If set to True, a filtering menu will be displayed. The default is True.
colorScale : str , optional
The desired type of plotly color scales to use (e.g. "viridis", "plasma"). The default is "viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
Returns
-------
None
The pyvis graph is displayed either inline (notebook mode) or in a new browser window or tab.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
from topologicpy.Color import Color
from os.path import exists
try:
from pyvis.network import Network
except:
print("Graph.PyvisGraph - Information: Installing required pyvis library.")
try:
os.system("pip install pyvis")
except:
os.system("pip install pyvis --user")
try:
from pyvis.network import Network
print("Graph.PyvisGraph - Information: pyvis library installed correctly.")
except:
warnings.warn("Graph - Error: Could not import pyvis. Please try to install pyvis manually. Retruning None.")
return None
net = Network(height=str(height)+"px", width="100%", bgcolor=backgroundColor, font_color=fontColor, select_menu=selectMenu, filter_menu=filterMenu, cdn_resources="remote", notebook=notebook)
if notebook == True:
net.prep_notebook()
vertices = Graph.Vertices(graph)
edges = Graph.Edges(graph)
nodes = [i for i in range(len(vertices))]
if not vertexLabelKey == None:
node_labels = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexLabelKey) for v in vertices]
else:
node_labels = list(range(len(vertices)))
if not vertexColorKey == None:
colors = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexColorKey) for v in vertices]
else:
colors = [vertexColor for v in vertices]
node_titles = [str(n) for n in node_labels]
group = ""
if not vertexGroupKey == None:
colors = []
if vertexGroups:
if len(vertexGroups) > 0:
if type(vertexGroups[0]) == int or type(vertexGroups[0]) == float:
if not minVertexGroup:
minVertexGroup = min(vertexGroups)
if not maxVertexGroup:
maxVertexGroup = max(vertexGroups)
else:
minVertexGroup = 0
maxVertexGroup = len(vertexGroups) - 1
else:
minVertexGroup = 0
maxVertexGroup = 1
for m, v in enumerate(vertices):
group = ""
d = Topology.Dictionary(v)
if d:
try:
group = Dictionary.ValueAtKey(d, key=vertexGroupKey) or None
except:
group = ""
try:
if type(group) == int or type(group) == float:
if group < minVertexGroup:
group = minVertexGroup
if group > maxVertexGroup:
group = maxVertexGroup
color = Color.RGBToHex(Color.ByValueInRange(group, minValue=minVertexGroup, maxValue=maxVertexGroup, colorScale=colorScale))
else:
color = Color.RGBToHex(Color.ByValueInRange(vertexGroups.index(group), minValue=minVertexGroup, maxValue=maxVertexGroup, colorScale=colorScale))
colors.append(color)
except:
colors.append(vertexColor)
net.add_nodes(nodes, label=node_labels, title=node_titles, color=colors)
for e in edges:
edge_label = ""
if not edgeLabelKey == None:
d = Topology.Dictionary(e)
edge_label = Dictionary.ValueAtKey(d, edgeLabelKey)
if edge_label == None:
edge_label = ""
w = edgeWeight
if not edgeWeightKey == None:
d = Topology.Dictionary(e)
if edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance":
w = Edge.Length(e)
else:
weightValue = Dictionary.ValueAtKey(d, edgeWeightKey)
if weightValue:
w = weightValue
sv = Edge.StartVertex(e)
ev = Edge.EndVertex(e)
svi = Vertex.Index(sv, vertices)
evi = Vertex.Index(ev, vertices)
net.add_edge(svi, evi, weight=w, label=edge_label)
net.inherit_edge_colors(False)
# add neighbor data to node hover data and compute vertexSize
if showNeighbours == True or not vertexSizeKey == None:
for i, node in enumerate(net.nodes):
if showNeighbours == True:
neighbors = list(net.neighbors(node["id"]))
neighbor_labels = [str(net.nodes[n]["id"])+": "+str(net.nodes[n]["label"]) for n in neighbors]
node["title"] = str(node["id"])+": "+node["title"]+"\n"
node["title"] += "Neighbors:\n" + "\n".join(neighbor_labels)
vs = vertexSize
if not vertexSizeKey == None:
d = Topology.Dictionary(vertices[i])
if vertexSizeKey.lower() == "neighbours" or vertexSizeKey.lower() == "degree":
temp_vs = Graph.VertexDegree(graph, vertices[i])
else:
temp_vs = Dictionary.ValueAtKey(vertices[i], vertexSizeKey)
if temp_vs:
vs = temp_vs
node["value"] = vs
# Make sure the file extension is .html
ext = path[len(path)-5:len(path)]
if ext.lower() != ".html":
path = path+".html"
if not overwrite and exists(path):
print("Graph.PyvisGraph - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
return None
if overwrite == True:
net.save_graph(path)
net.show_buttons()
net.show(path, notebook=notebook)
return None
@staticmethod
def RemoveEdge(graph, edge, tolerance=0.0001):
"""
Removes the input edge from the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
edge : topologic_core.Edge
The input edge.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph with the input edge removed.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.RemoveEdge - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(edge, "Edge"):
print("Graph.RemoveEdge - Error: The input edge is not a valid edge. Returning None.")
return None
_ = graph.RemoveEdges([edge], tolerance)
return graph
@staticmethod
def RemoveVertex(graph, vertex, tolerance=0.0001):
"""
Removes the input vertex from the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The input graph with the input vertex removed.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.RemoveVertex - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.RemoveVertex - Error: The input vertex is not a valid vertex. Returning None.")
return None
graphVertex = Graph.NearestVertex(graph, vertex)
_ = graph.RemoveVertices([graphVertex])
return graph
@staticmethod
def SetDictionary(graph, dictionary):
"""
Sets the input graph's dictionary to the input dictionary
Parameters
----------
graph : topologic_core.Graph
The input graph.
dictionary : topologic_core.Dictionary or dict
The input dictionary.
Returns
-------
topologic_core.Graph
The input graph with the input dictionary set in it.
"""
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.SetDictionary - Error: the input graph parameter is not a valid graph. Returning None.")
return None
if isinstance(dictionary, dict):
dictionary = Dictionary.ByPythonDictionary(dictionary)
if not Topology.IsInstance(dictionary, "Dictionary"):
print("Graph.SetDictionary - Warning: the input dictionary parameter is not a valid dictionary. Returning original input.")
return graph
if len(dictionary.Keys()) < 1:
print("Graph.SetDictionary - Warning: the input dictionary parameter is empty. Returning original input.")
return graph
_ = graph.SetDictionary(dictionary)
return graph
@staticmethod
def ShortestPath(graph, vertexA, vertexB, vertexKey="", edgeKey="Length", tolerance=0.0001):
"""
Returns the shortest path that connects the input vertices. The shortest path will take into consideration both the vertexKey and the edgeKey if both are specified and will minimize the total "cost" of the path. Otherwise, it will take into consideration only whatever key is specified.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
vertexKey : string , optional
The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None.
edgeKey : string , optional
The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length".
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Wire
The shortest path between the input vertices.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Wire import Wire
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ShortestPath - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.ShortestPath - Error: The input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.ShortestPath - Error: The input vertexB is not a valid vertex. Returning None.")
return None
if edgeKey:
if edgeKey.lower() == "length":
edgeKey = "Length"
try:
gsv = Graph.NearestVertex(graph, vertexA)
gev = Graph.NearestVertex(graph, vertexB)
shortest_path = graph.ShortestPath(gsv, gev, vertexKey, edgeKey)
if Topology.IsInstance(shortest_path, "Edge"):
shortest_path = Wire.ByEdges([shortest_path])
sv = Topology.Vertices(shortest_path)[0]
if Vertex.Distance(sv, gev) < tolerance: # Path is reversed. Correct it.
if Topology.IsInstance(shortest_path, "Wire"):
shortest_path = Wire.Reverse(shortest_path)
shortest_path = Wire.OrientEdges(shortest_path, Wire.StartVertex(shortest_path), tolerance=tolerance)
return shortest_path
except:
return None
@staticmethod
def ShortestPaths(graph, vertexA, vertexB, vertexKey="", edgeKey="length", timeLimit=10,
pathLimit=10, tolerance=0.0001):
"""
Returns the shortest path that connects the input vertices.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
vertexKey : string , optional
The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None.
edgeKey : string , optional
The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length".
timeLimit : int , optional
The search time limit in seconds. The default is 10 seconds
pathLimit: int , optional
The number of found paths limit. The default is 10 paths.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
list
The list of shortest paths between the input vertices.
"""
from topologicpy.Topology import Topology
def isUnique(paths, path):
if path == None:
return False
if len(paths) < 1:
return True
for aPath in paths:
copyPath = topologic.Topology.DeepCopy(aPath) # Hook to Core
dif = copyPath.Difference(path, False)
if dif == None:
return False
return True
if not Topology.IsInstance(graph, "Graph"):
print("Graph.ShortestPaths - Error: The input graph parameter is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.ShortestPaths - Error: The input vertexA parameter is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.ShortestPaths - Error: The input vertexB parameter is not a valid vertex. Returning None.")
return None
shortestPaths = []
end = time.time() + timeLimit
while time.time() < end and len(shortestPaths) < pathLimit:
if (graph != None):
if edgeKey:
if edgeKey.lower() == "length":
edgeKey = "Length"
shortest_path = Graph.ShortestPath(graph, vertexA, vertexB, vertexKey=vertexKey, edgeKey=edgeKey, tolerance=tolerance) # Find the first shortest path
if isUnique(shortestPaths, shortest_path):
shortestPaths.append(shortest_path)
vertices = Graph.Vertices(graph)
random.shuffle(vertices)
edges = Graph.Edges(graph)
graph = Graph.ByVerticesEdges(vertices, edges)
return shortestPaths
@staticmethod
def Show(graph, vertexColor="black", vertexSize=6, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=[], showVertices=True, showVertexLegend=False, edgeColor="black", edgeWidth=1, edgeLabelKey=None, edgeGroupKey=None, edgeGroups=[], showEdges=True, showEdgeLegend=False, colorScale='viridis', renderer=None,
width=950, height=500, xAxis=False, yAxis=False, zAxis=False, axisSize=1, backgroundColor='rgba(0,0,0,0)', marginLeft=0, marginRight=0, marginTop=20, marginBottom=0,
camera=[-1.25, -1.25, 1.25], center=[0, 0, 0], up=[0, 0, 1], projection="perspective", tolerance=0.0001):
"""
Shows the graph using Plotly.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexColor : str , optional
The desired color of the output vertices. This can be any plotly color string and may be specified as:
- A hex string (e.g. '#ff0000')
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
- A named CSS color.
The default is "black".
vertexSize : float , optional
The desired size of the vertices. The default is 1.1.
vertexLabelKey : str , optional
The dictionary key to use to display the vertex label. The default is None.
vertexGroupKey : str , optional
The dictionary key to use to display the vertex group. The default is None.
vertexGroups : list , optional
The list of vertex groups against which to index the color of the vertex. The default is [].
showVertices : bool , optional
If set to True the vertices will be drawn. Otherwise, they will not be drawn. The default is True.
showVertexLegend : bool , optional
If set to True the vertex legend will be drawn. Otherwise, it will not be drawn. The default is False.
edgeColor : str , optional
The desired color of the output edges. This can be any plotly color string and may be specified as:
- A hex string (e.g. '#ff0000')
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
- A named CSS color.
The default is "black".
edgeWidth : float , optional
The desired thickness of the output edges. The default is 1.
edgeLabelKey : str , optional
The dictionary key to use to display the edge label. The default is None.
edgeGroupKey : str , optional
The dictionary key to use to display the edge group. The default is None.
showEdges : bool , optional
If set to True the edges will be drawn. Otherwise, they will not be drawn. The default is True.
showEdgeLegend : bool , optional
If set to True the edge legend will be drawn. Otherwise, it will not be drawn. The default is False.
colorScale : str , optional
The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
renderer : str , optional
The desired renderer. See Plotly.Renderers(). If set to None, the code will attempt to discover the most suitable renderer. The default is None.
width : int , optional
The width in pixels of the figure. The default value is 950.
height : int , optional
The height in pixels of the figure. The default value is 950.
xAxis : bool , optional
If set to True the x axis is drawn. Otherwise it is not drawn. The default is False.
yAxis : bool , optional
If set to True the y axis is drawn. Otherwise it is not drawn. The default is False.
zAxis : bool , optional
If set to True the z axis is drawn. Otherwise it is not drawn. The default is False.
axisSize : float , optional
The size of the X, Y, Z, axes. The default is 1.
backgroundColor : str , optional
The desired color of the background. This can be any plotly color string and may be specified as:
- A hex string (e.g. '#ff0000')
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
- A named CSS color.
The default is "rgba(0,0,0,0)".
marginLeft : int , optional
The size in pixels of the left margin. The default value is 0.
marginRight : int , optional
The size in pixels of the right margin. The default value is 0.
marginTop : int , optional
The size in pixels of the top margin. The default value is 20.
marginBottom : int , optional
The size in pixels of the bottom margin. The default value is 0.
camera : list , optional
The desired location of the camera). The default is [-1.25, -1.25, 1.25].
center : list , optional
The desired center (camera target). The default is [0, 0, 0].
up : list , optional
The desired up vector. The default is [0, 0, 1].
projection : str , optional
The desired type of projection. The options are "orthographic" or "perspective". It is case insensitive. The default is "perspective"
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
None
"""
from topologicpy.Plotly import Plotly
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Show - Error: The input graph is not a valid graph. Returning None.")
return None
data= Plotly.DataByGraph(graph, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLegend=showEdgeLegend, colorScale=colorScale)
fig = Plotly.FigureByData(data, width=width, height=height, xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize, backgroundColor=backgroundColor,
marginLeft=marginLeft, marginRight=marginRight, marginTop=marginTop, marginBottom=marginBottom, tolerance=tolerance)
Plotly.Show(fig, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
@staticmethod
def Size(graph):
"""
Returns the graph size of the input graph. The graph size is its number of edges.
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
int
The number of edges in the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Size - Error: The input graph is not a valid graph. Returning None.")
return None
return len(Graph.Edges(graph))
@staticmethod
def TopologicalDistance(graph, vertexA, vertexB, tolerance=0.0001):
"""
Returns the topological distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory).
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexA : topologic_core.Vertex
The first input vertex.
vertexB : topologic_core.Vertex
The second input vertex.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
int
The topological distance between the input vertices.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.TopologicalDistance - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertexA, "Vertex"):
print("Graph.TopologicalDistance - Error: The input vertexA is not a valid vertex. Returning None.")
return None
if not Topology.IsInstance(vertexB, "Vertex"):
print("Graph.TopologicalDistance - Error: The input vertexB is not a valid vertex. Returning None.")
return None
return graph.TopologicalDistance(vertexA, vertexB, tolerance)
@staticmethod
def Topology(graph):
"""
Returns the topology (cluster) of the input graph
Parameters
----------
graph : topologic_core.Graph
The input graph.
Returns
-------
topologic_core.Cluster
The topology of the input graph.
"""
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Topology - Error: The input graph is not a valid graph. Returning None.")
return None
return graph.Topology()
@staticmethod
def Tree(graph, vertex=None, tolerance=0.0001):
"""
Creates a tree graph version of the input graph rooted at the input vertex.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex , optional
The input root vertex. If not set, the first vertex in the graph is set as the root vertex. The default is None.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The tree graph version of the input graph.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Topology import Topology
def vertexInList(vertex, vertexList):
if vertex and vertexList:
if Topology.IsInstance(vertex, "Vertex") and isinstance(vertexList, list):
for i in range(len(vertexList)):
if vertexList[i]:
if Topology.IsInstance(vertexList[i], "Vertex"):
if Topology.IsSame(vertex, vertexList[i]):
return True
return False
def getChildren(vertex, parent, graph, vertices):
children = []
adjVertices = []
if vertex:
adjVertices = Graph.AdjacentVertices(graph, vertex)
if parent == None:
return adjVertices
else:
for aVertex in adjVertices:
if (not vertexInList(aVertex, [parent])) and (not vertexInList(aVertex, vertices)):
children.append(aVertex)
return children
def buildTree(graph, dictionary, vertex, parent, tolerance=0.0001):
vertices = dictionary['vertices']
edges = dictionary['edges']
if not vertexInList(vertex, vertices):
vertices.append(vertex)
if parent:
edge = Graph.Edge(graph, parent, vertex, tolerance)
ev = Edge.EndVertex(edge)
if Vertex.Distance(parent, ev) < tolerance:
edge = Edge.Reverse(edge)
edges.append(edge)
if parent == None:
parent = vertex
children = getChildren(vertex, parent, graph, vertices)
dictionary['vertices'] = vertices
dictionary['edges'] = edges
for child in children:
dictionary = buildTree(graph, dictionary, child, vertex, tolerance)
return dictionary
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Tree - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
vertex = Graph.Vertices(graph)[0]
else:
vertex = Graph.NearestVertex(graph, vertex)
dictionary = {'vertices':[], 'edges':[]}
dictionary = buildTree(graph, dictionary, vertex, None, tolerance)
return Graph.ByVerticesEdges(dictionary['vertices'], dictionary['edges'])
@staticmethod
def VertexDegree(graph, vertex, edgeKey: str = None, tolerance: float = 0.0001):
"""
Returns the degree of the input vertex. See https://en.wikipedia.org/wiki/Degree_(graph_theory).
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertex : topologic_core.Vertex
The input vertex.
edgeKey : str , optional
If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate
the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
int
The degree of the input vertex.
"""
from topologicpy.Topology import Topology
from topologicpy.Dictionary import Dictionary
import numbers
if not Topology.IsInstance(graph, "Graph"):
print("Graph.VertexDegree - Error: The input graph is not a valid graph. Returning None.")
return None
if not Topology.IsInstance(vertex, "Vertex"):
print("Graph.VertexDegree - Error: The input vertex is not a valid vertex. Returning None.")
return None
if not isinstance(edgeKey, str):
edgeKey = ""
edges = Graph.Edges(graph, [vertex], tolerance=tolerance)
degree = 0
for edge in edges:
d = Topology.Dictionary(edge)
value = Dictionary.ValueAtKey(d, edgeKey)
if isinstance(value, numbers.Number):
degree += value
else:
degree += 1
return degree
@staticmethod
def Vertices(graph, vertexKey=None, reverse=False):
"""
Returns the list of vertices in the input graph.
Parameters
----------
graph : topologic_core.Graph
The input graph.
vertexKey : str , optional
If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None.
reverse : bool , optional
If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
Returns
-------
list
The list of vertices in the input graph.
"""
from topologicpy.Helper import Helper
from topologicpy.Dictionary import Dictionary
from topologicpy.Topology import Topology
if not Topology.IsInstance(graph, "Graph"):
print("Graph.Vertices - Error: The input graph is not a valid graph. Returning None.")
return None
vertices = []
if graph:
try:
_ = graph.Vertices(vertices)
except:
vertices = []
if not vertexKey == None:
sorting_values = []
for v in vertices:
d = Topology.Dictionary(v)
value = Dictionary.ValueAtKey(d, vertexKey)
sorting_values.append(value)
vertices = Helper.Sort(vertices, sorting_values)
if reverse == True:
vertices.reverse()
return vertices
@staticmethod
def VisibilityGraph(face, viewpointsA=None, viewpointsB=None, tolerance=0.0001):
"""
Creates a 2D visibility graph.
Parameters
----------
face : topologic_core.Face
The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles
viewpointsA : list , optional
The first input list of viewpoints (vertices). Visibility edges will connect these veritces to viewpointsB. If set to None, this parameters will be set to all vertices of the input face. The default is None.
viewpointsB : list , optional
The input list of viewpoints (vertices). Visibility edges will connect these vertices to viewpointsA. If set to None, this parameters will be set to all vertices of the input face. The default is None.
tolerance : float , optional
The desired tolerance. The default is 0.0001.
Returns
-------
topologic_core.Graph
The visibility graph.
"""
from topologicpy.Vertex import Vertex
from topologicpy.Edge import Edge
from topologicpy.Face import Face
from topologicpy.Graph import Graph
from topologicpy.Cluster import Cluster
from topologicpy.Topology import Topology
if not Topology.IsInstance(face, "Face"):
print("Graph.VisibilityGraph - Error: The input face parameter is not a valid face. Returning None")
return None
if viewpointsA == None:
viewpointsA = Topology.Vertices(face)
if viewpointsB == None:
viewpointsB = Topology.Vertices(face)
if not isinstance(viewpointsA, list):
print("Graph.VisibilityGraph - Error: The input viewpointsA parameter is not a valid list. Returning None")
return None
if not isinstance(viewpointsB, list):
print("Graph.VisibilityGraph - Error: The input viewpointsB parameter is not a valid list. Returning None")
return None
viewpointsA = [v for v in viewpointsA if Topology.IsInstance(v, "Vertex")]
if len(viewpointsA) < 1:
print("Graph.VisibilityGraph - Error: The input viewpointsA parameter does not contain any vertices. Returning None")
return None
viewpointsB = [v for v in viewpointsB if Topology.IsInstance(v, "Vertex")]
if len(viewpointsB) < 1: #Nothing to look at, so return a graph made of viewpointsA
return Graph.ByVerticesEdges(viewpointsA, [])
i_boundaries = Face.InternalBoundaries(face)
obstacles = []
for i_boundary in i_boundaries:
if Topology.IsInstance(i_boundary, "Wire"):
obstacles.append(Face.ByWire(i_boundary))
if len(obstacles) > 0:
obstacle_cluster = Cluster.ByTopologies(obstacles)
else:
obstacle_cluster = None
def intersects_obstacles(edge, obstacle_cluster, tolerance=0.0001):
result = Topology.Difference(edge, obstacle_cluster)
if result == None:
return True
if Topology.IsInstance(result, "Cluster"):
return True
if Topology.IsInstance(result, "Edge"):
if abs(Edge.Length(edge) - Edge.Length(result)) > tolerance:
return True
return False
final_edges = []
for i in tqdm(range(len(viewpointsA))):
va = viewpointsA[i]
for j in range(len(viewpointsB)):
vb = viewpointsB[j]
if Vertex.Distance(va, vb) > tolerance:
edge = Edge.ByVertices([va,vb])
if not intersects_obstacles(edge, obstacle_cluster):
final_edges.append(edge)
if len(final_edges) > 0:
final_vertices = Topology.Vertices(Cluster.ByTopologies(final_edges))
g = Graph.ByVerticesEdges(final_vertices, final_edges)
return g
return None
Classes
class Graph
-
Expand source code
class Graph: @staticmethod def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, tolerance=0.0001): """ Returns the adjacency matrix of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_matrix. Parameters ---------- graph : topologic_core.Graph The input graph. vertexKey : str , optional If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None. reverse : bool , optional If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False. edgeKeyFwd : str , optional If set, the value at this key in the connecting edge from start vertex to end verrtex (forward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting. edgeKeyBwd : str , optional If set, the value at this key in the connecting edge from end vertex to start verrtex (backward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting. bidirKey : bool , optional If set to True or False, this key in the connecting edge will be used to determine is the edge is supposed to be bidirectional or not. If set to None, the input variable bidrectional will be used instead. The default is None bidirectional : bool , optional If set to True, the edges in the graph that do not have a bidireKey in their dictionaries will be treated as being bidirectional. Otherwise, the start vertex and end vertex of the connecting edge will determine the direction. The default is True. useEdgeIndex : bool , False If set to True, the adjacency matrix values will the index of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used. useEdgeLength : bool , False If set to True, the adjacency matrix values will the length of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The adjacency matrix. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Helper import Helper if not Topology.IsInstance(graph, "Graph"): print("Graph.AdjacencyMatrix - Error: The input graph is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) if not vertexKey == None: sorting_values = [] for v in vertices: d = Topology.Dictionary(v) value = Dictionary.ValueAtKey(d, vertexKey) sorting_values.append(value) vertices = Helper.Sort(vertices, sorting_values) if reverse == True: vertices.reverse() edges = Graph.Edges(graph) order = len(vertices) matrix = [] # Initialize the matrix with zeroes for i in range(order): tempRow = [] for j in range(order): tempRow.append(0) matrix.append(tempRow) for i, edge in enumerate(edges): sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) svi = Vertex.Index(sv, vertices, tolerance=tolerance) evi = Vertex.Index(ev, vertices, tolerance=tolerance) if bidirKey == None: bidir = bidirectional else: bidir = Dictionary.ValueAtKey(Topology.Dictionary(edge), bidirKey) if bidir == None: bidir = bidirectional if edgeKeyFwd == None: valueFwd = 1 else: valueFwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyFwd) if valueFwd == None: valueFwd = 1 if edgeKeyBwd == None: valueBwd = 1 else: valueBwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyBwd) if valueBwd == None: valueBwd = 1 if useEdgeIndex: valueFwd = i+1 valueBwd = i+1 if useEdgeLength: valueFwd = Edge.Length(edge) valueBwd = Edge.Length(edge) matrix[svi][evi] = valueFwd if bidir: matrix[evi][svi] = valueBwd return matrix @staticmethod def AdjacencyList(graph, vertexKey=None, reverse=True, tolerance=0.0001): """ Returns the adjacency list of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_list. Parameters ---------- graph : topologic_core.Graph The input graph. vertexKey : str , optional If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None. reverse : bool , optional If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The adjacency list. """ from topologicpy.Vertex import Vertex from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology from topologicpy.Helper import Helper if not Topology.IsInstance(graph, "Graph"): print("Graph.AdjacencyList - Error: The input graph is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) if not vertexKey == None: sorting_values = [] for v in vertices: d = Topology.Dictionary(v) value = Dictionary.ValueAtKey(d, vertexKey) sorting_values.append(value) vertices = Helper.Sort(vertices, sorting_values) if reverse == True: vertices.reverse() order = len(vertices) adjList = [] for i in range(order): tempRow = [] v = Graph.NearestVertex(graph, vertices[i]) adjVertices = Graph.AdjacentVertices(graph, v) for adjVertex in adjVertices: adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance) if not adjIndex == None: tempRow.append(adjIndex) tempRow.sort() adjList.append(tempRow) return adjList @staticmethod def AddEdge(graph, edge, transferVertexDictionaries=False, transferEdgeDictionaries=False, tolerance=0.0001): """ Adds the input edge to the input Graph. Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input edge. transferDictionaries : bool, optional If set to True, the dictionaries of the edge and its vertices are transfered to the graph. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input edge added to it. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology def addIfUnique(graph_vertices, vertex, tolerance): unique = True returnVertex = vertex for gv in graph_vertices: if (Vertex.Distance(vertex, gv) < tolerance): if transferVertexDictionaries == True: gd = Topology.Dictionary(gv) vd = Topology.Dictionary(vertex) gk = gd.Keys() vk = vd.Keys() d = None if (len(gk) > 0) and (len(vk) > 0): d = Dictionary.ByMergedDictionaries([gd, vd]) elif (len(gk) > 0) and (len(vk) < 1): d = gd elif (len(gk) < 1) and (len(vk) > 0): d = vd if d: _ = Topology.SetDictionary(gv, d) unique = False returnVertex = gv break if unique: graph_vertices.append(vertex) return [graph_vertices, returnVertex] if not Topology.IsInstance(graph, "Graph"): print("Graph.AddEdge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.AddEdge - Error: The input edge is not a valid edge. Returning None.") return None graph_vertices = Graph.Vertices(graph) graph_edges = Graph.Edges(graph, graph_vertices, tolerance) vertices = Edge.Vertices(edge) new_vertices = [] for vertex in vertices: graph_vertices, nv = addIfUnique(graph_vertices, vertex, tolerance) new_vertices.append(nv) new_edge = Edge.ByVertices([new_vertices[0], new_vertices[1]], tolerance=tolerance) if transferEdgeDictionaries == True: d = Topology.Dictionary(edge) keys = Dictionary.Keys(d) if isinstance(keys, list): if len(keys) > 0: _ = Topology.SetDictionary(new_edge, d) graph_edges.append(new_edge) new_graph = Graph.ByVerticesEdges(graph_vertices, graph_edges) return new_graph @staticmethod def AddVertex(graph, vertex, tolerance=0.0001): """ Adds the input vertex to the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input vertex added to it. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AddVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.AddVertex - Error: The input vertex is not a valid vertex. Returning None.") return None _ = graph.AddVertices([vertex], tolerance) return graph @staticmethod def AddVertices(graph, vertices, tolerance=0.0001): """ Adds the input vertex to the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list The input list of vertices. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input vertex added to it. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AddVertices - Error: The input graph is not a valid graph. Returning None.") return None if not isinstance(vertices, list): print("Graph.AddVertices - Error: The input list of vertices is not a valid list. Returning None.") return None vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.AddVertices - Error: Could not find any valid vertices in the input list of vertices. Returning None.") return None _ = graph.AddVertices(vertices, tolerance) return graph @staticmethod def AdjacentVertices(graph, vertex): """ Returns the list of vertices connected to the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex the input vertex. Returns ------- list The list of adjacent vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AdjacentVertices - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.AdjacentVertices - Error: The input vertex is not a valid vertex. Returning None.") return None vertices = [] _ = graph.AdjacentVertices(vertex, vertices) return list(vertices) @staticmethod def AllPaths(graph, vertexA, vertexB, timeLimit=10): """ Returns all the paths that connect the input vertices within the allowed time limit in seconds. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. timeLimit : int , optional The time limit in second. The default is 10 seconds. Returns ------- list The list of all paths (wires) found within the time limit. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AllPaths - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.AllPaths - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.AllPaths - Error: The input vertexB is not a valid vertex. Returning None.") return None paths = [] _ = graph.AllPaths(vertexA, vertexB, True, timeLimit, paths) return paths @staticmethod def AverageClusteringCoefficient(graph, mantissa=6): """ Returns the average clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient. Parameters ---------- graph : topologic_core.Graph The input graph. mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- float The average clustering coefficient of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) if len(vertices) < 1: print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is a NULL graph. Returning None.") return None if len(vertices) == 1: return 0.0 lcc = Graph.LocalClusteringCoefficient(graph, vertices) acc = round(sum(lcc)/float(len(lcc)), mantissa) return acc @staticmethod def BOTGraph(graph, bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel = "Site_0001", siteDictionary = None, buildingLabel = "Building_0001", buildingDictionary = None , storeyPrefix = "Storey", floorLevels =[], labelKey="label", typeKey="type", geometryKey="brep", spaceType = "space", wallType = "wall", slabType = "slab", doorType = "door", windowType = "window", contentType = "content" ): """ Creates an RDF graph according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/. Parameters ---------- graph : topologic_core.Graph The input graph. bidirectional : bool , optional If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False. includeAttributes : bool , optional If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False. includeLabel : bool , optional If set to True, a label is attached to each node. Otherwise, it is not. The default is False. includeGeometry : bool , optional If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False. siteLabel : str , optional The desired site label. The default is "Site_0001". siteDictionary : dict , optional The dictionary of site attributes to include in the output. The default is None. buildingLabel : str , optional The desired building label. The default is "Building_0001". buildingDictionary : dict , optional The dictionary of building attributes to include in the output. The default is None. storeyPrefix : str , optional The desired prefixed to use for each building storey. The default is "Storey". floorLevels : list , optional The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute. labelKey : str , optional The dictionary key to use to look up the label of the node. The default is "label". typeKey : str , optional The dictionary key to use to look up the type of the node. The default is "type". geometryKey : str , optional The dictionary key to use to look up the geometry of the node. The default is "brep". spaceType : str , optional The dictionary string value to use to look up vertices of type "space". The default is "space". wallType : str , optional The dictionary string value to use to look up vertices of type "wall". The default is "wall". slabType : str , optional The dictionary string value to use to look up vertices of type "slab". The default is "slab". doorType : str , optional The dictionary string value to use to look up vertices of type "door". The default is "door". windowType : str , optional The dictionary string value to use to look up vertices of type "window". The default is "window". contentType : str , optional The dictionary string value to use to look up vertices of type "content". The default is "contents". Returns ------- rdflib.graph.Graph The rdf graph using the BOT ontology. """ from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import os import warnings try: from rdflib import Graph as RDFGraph from rdflib import URIRef, Literal, Namespace from rdflib.namespace import RDF, RDFS except: print("Graph.BOTGraph - Information: Installing required rdflib library.") try: os.system("pip install rdflib") except: os.system("pip install rdflib --user") try: from rdflib import Graph as RDFGraph from rdflib import URIRef, Literal, Namespace from rdflib.namespace import RDF, RDFS print("Graph.BOTGraph - Information: rdflib library installed correctly.") except: warnings.warn("Graph.BOTGraph - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.") return None if floorLevels == None: floorLevels = [] json_data = Graph.JSONData(graph, vertexLabelKey=labelKey) # Create an empty RDF graph bot_graph = RDFGraph() # Define namespaces rdf = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#") bot = Namespace("https://w3id.org/bot#") # Define a custom prefix mapping bot_graph.namespace_manager.bind("bot", bot) # Add site site_uri = URIRef(siteLabel) bot_graph.add((site_uri, rdf.type, bot.Site)) if includeLabel: bot_graph.add((site_uri, RDFS.label, Literal(siteLabel))) if Topology.IsInstance(siteDictionary, "Dictionary"): keys = Dictionary.Keys(siteDictionary) for key in keys: value = Dictionary.ValueAtKey(siteDictionary, key) if not (key == labelKey) and not (key == typeKey): bot_graph.add((site_uri, bot[key], Literal(value))) # Add building building_uri = URIRef(buildingLabel) bot_graph.add((building_uri, rdf.type, bot.Building)) if includeLabel: bot_graph.add((building_uri, RDFS.label, Literal(buildingLabel))) if Topology.IsInstance(buildingDictionary, "Dictionary"): keys = Dictionary.Keys(buildingDictionary) for key in keys: value = Dictionary.ValueAtKey(buildingDictionary, key) if key == labelKey: if includeLabel: bot_graph.add((building_uri, RDFS.label, Literal(value))) elif key != typeKey: bot_graph.add((building_uri, bot[key], Literal(value))) # Add stories # if floor levels are not given, then need to be computed if len(floorLevels) == 0: for node, attributes in json_data['vertices'].items(): if slabType.lower() in attributes[typeKey].lower(): floorLevels.append(attributes["z"]) floorLevels = list(set(floorLevels)) floorLevels.sort() floorLevels = floorLevels[:-1] storey_uris = [] n = max(len(str(len(floorLevels))),4) for i, floor_level in enumerate(floorLevels): storey_uri = URIRef(storeyPrefix+"_"+str(i+1).zfill(n)) bot_graph.add((storey_uri, rdf.type, bot.Storey)) if includeLabel: bot_graph.add((storey_uri, RDFS.label, Literal(storeyPrefix+"_"+str(i+1).zfill(n)))) storey_uris.append(storey_uri) # Add triples to relate building to site and stories to building bot_graph.add((site_uri, bot.hasBuilding, building_uri)) if bidirectional: bot_graph.add((building_uri, bot.isPartOf, site_uri)) # might not be needed for storey_uri in storey_uris: bot_graph.add((building_uri, bot.hasStorey, storey_uri)) if bidirectional: bot_graph.add((storey_uri, bot.isPartOf, building_uri)) # might not be needed # Add vertices as RDF resources for node, attributes in json_data['vertices'].items(): node_uri = URIRef(node) if spaceType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Space)) # Find the storey it is on z = attributes["z"] level = Helper.Position(z, floorLevels) if level > len(storey_uris): level = len(storey_uris) storey_uri = storey_uris[level-1] bot_graph.add((storey_uri, bot.hasSpace, node_uri)) if bidirectional: bot_graph.add((node_uri, bot.isPartOf, storey_uri)) # might not be needed elif windowType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Window)) elif doorType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Door)) elif wallType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Wall)) elif slabType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Slab)) else: bot_graph.add((node_uri, rdf.type, bot.Element)) if includeAttributes: for key, value in attributes.items(): if key == geometryKey: if includeGeometry: bot_graph.add((node_uri, bot.hasSimpleGeometry, Literal(value))) if key == labelKey: if includeLabel: bot_graph.add((node_uri, RDFS.label, Literal(value))) elif key != typeKey and key != geometryKey: bot_graph.add((node_uri, bot[key], Literal(value))) if includeLabel: for key, value in attributes.items(): if key == labelKey: bot_graph.add((node_uri, RDFS.label, Literal(value))) # Add edges as RDF triples for edge, attributes in json_data['edges'].items(): source = attributes["source"] target = attributes["target"] source_uri = URIRef(source) target_uri = URIRef(target) if spaceType.lower() in json_data['vertices'][source][typeKey].lower() and spaceType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((source_uri, bot.adjacentTo, target_uri)) if bidirectional: bot_graph.add((target_uri, bot.adjacentTo, source_uri)) elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and wallType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((target_uri, bot.interfaceOf, source_uri)) elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and slabType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((target_uri, bot.interfaceOf, source_uri)) elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and contentType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((source_uri, bot.containsElement, target_uri)) if bidirectional: bot_graph.add((target_uri, bot.isPartOf, source_uri)) else: bot_graph.add((source_uri, bot.connectsTo, target_uri)) if bidirectional: bot_graph.add((target_uri, bot.connectsTo, source_uri)) return bot_graph @staticmethod def BOTString(graph, format="turtle", bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel = "Site_0001", siteDictionary = None, buildingLabel = "Building_0001", buildingDictionary = None , storeyPrefix = "Storey", floorLevels =[], labelKey="label", typeKey="type", geometryKey="brep", spaceType = "space", wallType = "wall", slabType = "slab", doorType = "door", windowType = "window", contentType = "content", ): """ Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/. Parameters ---------- graph : topologic_core.Graph The input graph. format : str , optional The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads bidirectional : bool , optional If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False. includeAttributes : bool , optional If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False. includeLabel : bool , optional If set to True, a label is attached to each node. Otherwise, it is not. The default is False. includeGeometry : bool , optional If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False. siteLabel : str , optional The desired site label. The default is "Site_0001". siteDictionary : dict , optional The dictionary of site attributes to include in the output. The default is None. buildingLabel : str , optional The desired building label. The default is "Building_0001". buildingDictionary : dict , optional The dictionary of building attributes to include in the output. The default is None. storeyPrefix : str , optional The desired prefixed to use for each building storey. The default is "Storey". floorLevels : list , optional The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute. typeKey : str , optional The dictionary key to use to look up the type of the node. The default is "type". labelKey : str , optional The dictionary key to use to look up the label of the node. The default is "label". spaceType : str , optional The dictionary string value to use to look up vertices of type "space". The default is "space". wallType : str , optional The dictionary string value to use to look up vertices of type "wall". The default is "wall". slabType : str , optional The dictionary string value to use to look up vertices of type "slab". The default is "slab". doorType : str , optional The dictionary string value to use to look up vertices of type "door". The default is "door". windowType : str , optional The dictionary string value to use to look up vertices of type "window". The default is "window". contentType : str , optional The dictionary string value to use to look up vertices of type "content". The default is "contents". Returns ------- str The rdf graph serialized string using the BOT ontology. """ bot_graph = Graph.BOTGraph(graph, bidirectional=bidirectional, includeAttributes=includeAttributes, includeLabel=includeLabel, includeGeometry=includeGeometry, siteLabel=siteLabel, siteDictionary=siteDictionary, buildingLabel=buildingLabel, buildingDictionary=buildingDictionary, storeyPrefix=storeyPrefix, floorLevels=floorLevels, labelKey=labelKey, typeKey=typeKey, geometryKey=geometryKey, spaceType = spaceType, wallType = wallType, slabType = slabType, doorType = doorType, windowType = windowType, contentType = contentType ) return bot_graph.serialize(format=format) @staticmethod def BetweenessCentrality(graph, vertices=None, sources=None, destinations=None, tolerance=0.001): """ Returns the betweeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the betweeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Betweenness_centrality. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. The default is None which means all vertices in the input graph are considered. sources : list , optional The input list of source vertices. The default is None which means all vertices in the input graph are considered. destinations : list , optional The input list of destination vertices. The default is None which means all vertices in the input graph are considered. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The betweeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology def betweeness(vertices, topologies, tolerance=0.001): returnList = [0] * len(vertices) for topology in topologies: t_vertices = Topology.Vertices(topology) for t_v in t_vertices: index = Vertex.Index(t_v, vertices, strict=False, tolerance=tolerance) if not index == None: returnList[index] = returnList[index]+1 return returnList if not Topology.IsInstance(graph, "Graph"): print("Graph.BetweenessCentrality - Error: The input graph is not a valid graph. Returning None.") return None graphVertices = Graph.Vertices(graph) if not isinstance(vertices, list): vertices = graphVertices else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.BetweenessCentrality - Error: The input list of vertices does not contain valid vertices. Returning None.") return None if not isinstance(sources, list): sources = graphVertices else: sources = [v for v in sources if Topology.IsInstance(v, "Vertex")] if len(sources) < 1: print("Graph.BetweenessCentrality - Error: The input list of sources does not contain valid vertices. Returning None.") return None if not isinstance(destinations, list): destinations = graphVertices else: destinations = [v for v in destinations if Topology.IsInstance(v, "Vertex")] if len(destinations) < 1: print("Graph.BetweenessCentrality - Error: The input list of destinations does not contain valid vertices. Returning None.") return None paths = [] try: for so in tqdm(sources, desc="Computing Shortest Paths", leave=False): v1 = Graph.NearestVertex(graph, so) for si in destinations: v2 = Graph.NearestVertex(graph, si) if not v1 == v2: path = Graph.ShortestPath(graph, v1, v2) if path: paths.append(path) except: for so in sources: v1 = Graph.NearestVertex(graph, so) for si in destinations: v2 = Graph.NearestVertex(graph, si) if not v1 == v2: path = Graph.ShortestPath(graph, v1, v2) if path: paths.append(path) values = betweeness(vertices, paths, tolerance=tolerance) minValue = min(values) maxValue = max(values) size = maxValue - minValue values = [(v-minValue)/size for v in values] return values @staticmethod def ByAdjacencyMatrixCSVPath(path): """ Returns graphs according to the input path. This method assumes the CSV files follow an adjacency matrix schema. Parameters ---------- path : str The file path to the adjacency matrix CSV file. Returns ------- topologic_core.Graph The created graph. """ # Read the adjacency matrix from CSV file using pandas adjacency_matrix_df = pd.read_csv(path, header=None) # Convert DataFrame to a nested list adjacency_matrix = adjacency_matrix_df.values.tolist() return Graph.ByAdjacencyMatrix(adjacencyMatrix=adjacency_matrix) @staticmethod def ByAdjacencyMatrix(adjacencyMatrix, xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5): """ Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema. Parameters ---------- adjacencyMatrix : list The adjacency matrix expressed as a nested list of 0s and 1s. xMin : float, optional The desired minimum value to assign for a vertex's X coordinate. The default is -0.5. yMin : float, optional The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5. zMin : float, optional The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5. xMax : float, optional The desired maximum value to assign for a vertex's X coordinate. The default is 0.5. yMax : float, optional The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5. zMax : float, optional The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge import random if not isinstance(adjacencyMatrix, list): print("Graph.ByAdjacencyMatrix - Error: The input adjacencyMatrix parameter is not a valid list. Returning None.") return None # Add vertices with random coordinates vertices = [] for i in range(len(adjacencyMatrix)): x, y, z = random.uniform(xMin,xMax), random.uniform(yMin,yMax), random.uniform(zMin,zMax) vertices.append(Vertex.ByCoordinates(x, y, z)) # Create the graph using vertices and edges if len(vertices) == 0: print("Graph.ByAdjacencyMatrix - Error: The graph does not contain any vertices. Returning None.") return None # Add edges based on the adjacency matrix edges = [] for i in range(len(adjacencyMatrix)): for j in range(i+1, len(adjacencyMatrix)): if not adjacencyMatrix[i][j] == 0: edges.append(Edge.ByVertices([vertices[i], vertices[j]])) return Graph.ByVerticesEdges(vertices, edges) @staticmethod def ByBOTGraph(botGraph, includeContext = False, xMin = -0.5, xMax = 0.5, yMin = -0.5, yMax = 0.5, zMin = -0.5, zMax = 0.5, tolerance = 0.0001 ): def value_by_string(s): if s.lower() == "true": return True if s.lower() == "false": return False vt = "str" s2 = s.strip("-") if s2.isnumeric(): vt = "int" else: try: s3 = s2.split(".")[0] s4 = s2.split(".")[1] if (s3.isnumeric() or s4.isnumeric()): vt = "float" except: vt = "str" if vt == "str": return s elif vt == "int": return int(s) elif vt == "float": return float(s) def collect_nodes_by_type(rdf_graph, node_type=None): results = set() if node_type is not None: for subj, pred, obj in rdf_graph.triples((None, None, None)): if "type" in pred.lower(): if node_type.lower() in obj.lower(): results.add(subj) return list(results) def collect_attributes_for_subject(rdf_graph, subject): attributes = {} for subj, pred, obj in rdf_graph.triples((subject, None, None)): predicate_str = str(pred) object_str = str(obj) attributes[predicate_str] = object_str return attributes def get_triples_by_predicate_type(rdf_graph, predicate_type): triples = [] for subj, pred, obj in rdf_graph: if pred.split('#')[-1].lower() == predicate_type.lower(): triples.append((str(subj), str(pred), str(obj))) return triples from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Graph import Graph from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import random try: import rdflib except: print("Graph.BOTGraph - Information: Installing required rdflib library.") try: os.system("pip install rdflib") except: os.system("pip install rdflib --user") try: import rdflib print("Graph.BOTGraph - Information: rdflib library installed correctly.") except: warnings.warn("Graph.BOTGraph - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.") return None predicates = ['adjacentto', 'interfaceof', 'containselement', 'connectsto'] bot_types = ['Space', 'Wall', 'Slab', 'Door', 'Window', 'Element'] if includeContext: predicates += ['hasspace', 'hasbuilding', 'hasstorey'] bot_types += ['Site', 'Building', 'Storey'] namespaces = botGraph.namespaces() for ns in namespaces: if 'bot' in ns[0].lower(): bot_namespace = ns break ref = bot_namespace[1] nodes = [] for bot_type in bot_types: node_type = rdflib.term.URIRef(ref+bot_type) nodes +=collect_nodes_by_type(botGraph, node_type=node_type) vertices = [] dic = {} for node in nodes: x, y, z = random.uniform(xMin,xMax), random.uniform(yMin,yMax), random.uniform(zMin,zMax) d_keys = ["bot_id"] d_values = [str(node)] attributes = collect_attributes_for_subject(botGraph, node) keys = attributes.keys() for key in keys: key_type = key.split('#')[-1] if key_type.lower() not in predicates: if 'x' == key_type.lower(): x = value_by_string(attributes[key]) d_keys.append('x') d_values.append(x) elif 'y' == key_type.lower(): y = value_by_string(attributes[key]) d_keys.append('y') d_values.append(y) elif 'z' == key_type.lower(): z = value_by_string(attributes[key]) d_keys.append('z') d_values.append(z) else: d_keys.append(key_type.lower()) d_values.append(value_by_string(attributes[key].split("#")[-1])) d = Dictionary.ByKeysValues(d_keys, d_values) v = Vertex.ByCoordinates(x,y,z) v = Topology.SetDictionary(v, d) dic[str(node)] = v vertices.append(v) edges = [] for predicate in predicates: triples = get_triples_by_predicate_type(botGraph, predicate) for triple in triples: subj = triple[0] obj = triple[2] sv = dic[subj] ev = dic[obj] e = Edge.ByVertices([sv,ev], tolerance=tolerance) d = Dictionary.ByKeyValue("type", predicate) e = Topology.SetDictionary(e, d) edges.append(e) return Graph.ByVerticesEdges(vertices, edges) @staticmethod def ByBOTPath(path, includeContext = False, xMin = -0.5, xMax = 0.5, yMin = -0.5, yMax = 0.5, zMin = -0.5, zMax = 0.5, tolerance = 0.0001 ): try: from rdflib import Graph as RDFGraph except: print("Graph.ByBOTPath - Information: Installing required rdflib library.") try: os.system("pip install rdflib") except: os.system("pip install rdflib --user") try: from rdflib import Graph as RDFGraph print("Graph.ByBOTPath - Information: rdflib library installed correctly.") except: warnings.warn("Graph.ByBOTPath - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.") return None bot_graph = RDFGraph() bot_graph.parse(path) return Graph.ByBOTGraph(bot_graph, includeContext = includeContext, xMin = xMin, xMax = xMax, yMin = yMin, yMax = yMax, zMin = zMin, zMax = zMax, tolerance = tolerance ) @staticmethod def ByCSVPath(path, graphIDHeader="graph_id", graphLabelHeader="label", graphFeaturesHeader="feat", graphFeaturesKeys=[], edgeSRCHeader="src_id", edgeDSTHeader="dst_id", edgeLabelHeader="label", edgeTrainMaskHeader="train_mask", edgeValidateMaskHeader="val_mask", edgeTestMaskHeader="test_mask", edgeFeaturesHeader="feat", edgeFeaturesKeys=[], nodeIDHeader="node_id", nodeLabelHeader="label", nodeTrainMaskHeader="train_mask", nodeValidateMaskHeader="val_mask", nodeTestMaskHeader="test_mask", nodeFeaturesHeader="feat", nodeXHeader="X", nodeYHeader="Y", nodeZHeader="Z", nodeFeaturesKeys=[], tolerance=0.0001): """ Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema. Parameters ---------- path : str The path to the folder containing the .yaml and .csv files for graphs, edges, and nodes. graphIDHeader : str , optional The column header string used to specify the graph id. The default is "graph_id". graphLabelHeader : str , optional The column header string used to specify the graph label. The default is "label". graphFeaturesHeader : str , optional The column header string used to specify the graph features. The default is "feat". edgeSRCHeader : str , optional The column header string used to specify the source vertex id of edges. The default is "src_id". edgeDSTHeader : str , optional The column header string used to specify the destination vertex id of edges. The default is "dst_id". edgeLabelHeader : str , optional The column header string used to specify the label of edges. The default is "label". edgeTrainMaskHeader : str , optional The column header string used to specify the train mask of edges. The default is "train_mask". edgeValidateMaskHeader : str , optional The column header string used to specify the validate mask of edges. The default is "val_mask". edgeTestMaskHeader : str , optional The column header string used to specify the test mask of edges. The default is "test_mask". edgeFeaturesHeader : str , optional The column header string used to specify the features of edges. The default is "feat". edgeFeaturesKeys : list , optional The list of dicitonary keys to use to index the edge features. The length of this list must match the length of edge features. The default is []. nodeIDHeader : str , optional The column header string used to specify the id of nodes. The default is "node_id". nodeLabelHeader : str , optional The column header string used to specify the label of nodes. The default is "label". nodeTrainMaskHeader : str , optional The column header string used to specify the train mask of nodes. The default is "train_mask". nodeValidateMaskHeader : str , optional The column header string used to specify the validate mask of nodes. The default is "val_mask". nodeTestMaskHeader : str , optional The column header string used to specify the test mask of nodes. The default is "test_mask". nodeFeaturesHeader : str , optional The column header string used to specify the features of nodes. The default is "feat". nodeXHeader : str , optional The column header string used to specify the X coordinate of nodes. The default is "X". nodeYHeader : str , optional The column header string used to specify the Y coordinate of nodes. The default is "Y". nodeZHeader : str , optional The column header string used to specify the Z coordinate of nodes. The default is "Z". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- dict The dictionary of DGL graphs and labels found in the input CSV files. The keys in the dictionary are "graphs", "labels", "features" """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary import os from os.path import exists, isdir import yaml import glob import random import numbers def find_yaml_files(folder_path): yaml_files = glob.glob(f"{folder_path}/*.yaml") return yaml_files def read_yaml(file_path): with open(file_path, 'r') as file: data = yaml.safe_load(file) edge_data = data.get('edge_data', []) node_data = data.get('node_data', []) graph_data = data.get('graph_data', {}) edges_path = edge_data[0].get('file_name') if edge_data else None nodes_path = node_data[0].get('file_name') if node_data else None graphs_path = graph_data.get('file_name') return graphs_path, edges_path, nodes_path if not exists(path): print("Graph.ByCSVPath - Error: the input path parameter does not exists. Returning None.") return None if not isdir(path): print("Graph.ByCSVPath - Error: the input path parameter is not a folder. Returning None.") return None yaml_files = find_yaml_files(path) if len(yaml_files) < 1: print("Graph.ByCSVPath - Error: the input path parameter does not contain any valid YAML files. Returning None.") return None yaml_file = yaml_files[0] yaml_file_path = os.path.join(path, yaml_file) graphs_path, edges_path, nodes_path = read_yaml(yaml_file_path) if not graphs_path == None: graphs_path = os.path.join(path, graphs_path) if graphs_path == None: print("Graph.ByCSVPath - Warning: a graphs.csv file does not exist inside the folder specified by the input path parameter. Will assume the dataset includes only one graph.") graphs_df = pd.DataFrame() graph_ids=[0] graph_labels=[0] graph_features=[None] else: graphs_df = pd.read_csv(graphs_path) graph_ids = [] graph_labels = [] graph_features = [] if not edges_path == None: edges_path = os.path.join(path, edges_path) if not exists(edges_path): print("Graph.ByCSVPath - Error: an edges.csv file does not exist inside the folder specified by the input path parameter. Returning None.") return None edges_path = os.path.join(path, edges_path) edges_df = pd.read_csv(edges_path) grouped_edges = edges_df.groupby(graphIDHeader) if not nodes_path == None: nodes_path = os.path.join(path, nodes_path) if not exists(nodes_path): print("Graph.ByCSVPath - Error: a nodes.csv file does not exist inside the folder specified by the input path parameter. Returning None.") return None nodes_df = pd.read_csv(nodes_path) # Group nodes and nodes by their 'graph_id' grouped_nodes = nodes_df.groupby(graphIDHeader) if len(nodeFeaturesKeys) == 0: node_keys = [nodeIDHeader, nodeLabelHeader, "mask", nodeFeaturesHeader] else: node_keys = [nodeIDHeader, nodeLabelHeader, "mask"]+nodeFeaturesKeys if len(edgeFeaturesKeys) == 0: edge_keys = [edgeLabelHeader, "mask", edgeFeaturesHeader] else: edge_keys = [edgeLabelHeader, "mask"]+edgeFeaturesKeys if len(graphFeaturesKeys) == 0: graph_keys = [graphIDHeader, graphLabelHeader, graphFeaturesHeader] else: graph_keys = [graphIDHeader, graphLabelHeader]+graphFeaturesKeys # Iterate through the graphs DataFrame for index, row in graphs_df.iterrows(): graph_ids.append(row[graphIDHeader]) graph_labels.append(row[graphLabelHeader]) graph_features.append(row[graphFeaturesHeader]) vertices_ds = [] # A list to hold the vertices data structures until we can build the actual graphs # Iterate through the grouped nodes DataFrames for graph_id, group_node_df in grouped_nodes: vertices = [] verts = [] #This is a list of x, y, z tuples to make sure the vertices have unique locations. n_verts = 0 for index, row in group_node_df.iterrows(): n_verts += 1 node_id = row[nodeIDHeader] label = row[nodeLabelHeader] train_mask = row[nodeTrainMaskHeader] val_mask = row[nodeValidateMaskHeader] test_mask = row[nodeTestMaskHeader] mask = 0 if [train_mask, val_mask, test_mask] == [True, False, False]: mask = 0 elif [train_mask, val_mask, test_mask] == [False, True, False]: mask = 1 elif [train_mask, val_mask, test_mask] == [False, False, True]: mask = 2 else: mask = 0 features = row[nodeFeaturesHeader] x = row[nodeXHeader] y = row[nodeYHeader] z = row[nodeZHeader] if not isinstance(x, numbers.Number): x = random.randrange(0,1000) if not isinstance(y, numbers.Number): y = random.randrange(0,1000) if not isinstance(z, numbers.Number): z = random.randrange(0,1000) while [x, y, z] in verts: x = x + random.randrange(10000,30000,1000) y = y + random.randrange(4000,6000, 100) z = z + random.randrange(70000,90000, 1000) verts.append([x, y, z]) v = Vertex.ByCoordinates(x, y, z) if Topology.IsInstance(v, "Vertex"): if len(nodeFeaturesKeys) == 0: values = [node_id, label, mask, features] else: values = [node_id, label, mask] featureList = features.split(",") featureList = [float(s) for s in featureList] values = [node_id, label, mask]+featureList d = Dictionary.ByKeysValues(node_keys, values) if Topology.IsInstance(d, "Dictionary"): v = Topology.SetDictionary(v, d) else: print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created vertex.") vertices.append(v) else: print("Graph.ByCSVPath - Warning: Failed to create and add a vertex to the list of vertices.") vertices_ds.append(vertices) edges_ds = [] # A list to hold the vertices data structures until we can build the actual graphs # Access specific columns within the grouped DataFrame for graph_id, group_edge_df in grouped_edges: #vertices = vertices_ds[graph_id] edges = [] es = [] # a list to check for duplicate edges duplicate_edges = 0 for index, row in group_edge_df.iterrows(): src_id = int(row[edgeSRCHeader]) dst_id = int(row[edgeDSTHeader]) label = row[nodeLabelHeader] train_mask = row[edgeTrainMaskHeader] val_mask = row[edgeValidateMaskHeader] test_mask = row[edgeTestMaskHeader] mask = 0 if [train_mask, val_mask, test_mask] == [True, False, False]: mask = 0 elif [train_mask, val_mask, test_mask] == [False, True, False]: mask = 1 elif [train_mask, val_mask, test_mask] == [False, False, True]: mask = 2 else: mask = 0 features = row[edgeFeaturesHeader] if len(edgeFeaturesKeys) == 0: values = [label, mask, features] else: featureList = features.split(",") featureList = [float(s) for s in featureList] values = [label, mask]+featureList if not (src_id == dst_id) and not [src_id, dst_id] in es and not [dst_id, src_id] in es: es.append([src_id, dst_id]) edge = Edge.ByVertices([vertices[src_id], vertices[dst_id]], tolerance=tolerance) if Topology.IsInstance(edge, "Edge"): d = Dictionary.ByKeysValues(edge_keys, values) if Topology.IsInstance(d, "Dictionary"): edge = Topology.SetDictionary(edge, d) else: print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created edge.") edges.append(edge) else: print("Graph.ByCSVPath - Warning: Failed to create and add an edge to the list of edges.") else: duplicate_edges += 1 if duplicate_edges > 0: print("Graph.ByCSVPath - Warning: Found", duplicate_edges, "duplicate edges in graph id:", graph_id) edges_ds.append(edges) # Build the graphs graphs = [] for i, vertices, in enumerate(vertices_ds): edges = edges_ds[i] g = Graph.ByVerticesEdges(vertices, edges) temp_v = Graph.Vertices(g) temp_e = Graph.Edges(g) if Topology.IsInstance(g, "Graph"): if len(graphFeaturesKeys) == 0: values = [graph_ids[i], graph_labels[i], graph_features[i]] else: values = [graph_ids[i], graph_labels[i]] featureList = graph_features[i].split(",") featureList = [float(s) for s in featureList] values = [graph_ids[i], graph_labels[i]]+featureList l1 = len(graph_keys) l2 = len(values) if not l1 == l2: print("Graph.ByCSVPath - Error: The length of the keys and values lists do not match. Returning None.") return None d = Dictionary.ByKeysValues(graph_keys, values) if Topology.IsInstance(d, "Dictionary"): g = Graph.SetDictionary(g, d) else: print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created graph.") graphs.append(g) else: print("Graph.ByCSVPath - Error: Failed to create and add a graph to the list of graphs.") return {"graphs": graphs, "labels": graph_labels, "features": graph_features} @staticmethod def ByDGCNNFile(file, key: str = "label", tolerance: float = 0.0001): """ Creates a graph from a DGCNN File. Parameters ---------- file : file object The input file. key : str , optional The desired key for storing the node label. The default is "label". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- dict A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'. """ if not file: print("Graph.ByDGCNNFile - Error: The input file is not a valid file. Returning None.") return None dgcnn_string = file.read() file.close() return Graph.ByDGCNNString(dgcnn_string, key=key, tolerance=tolerance) @staticmethod def ByDGCNNPath(path, key: str = "label", tolerance: float = 0.0001): """ Creates a graph from a DGCNN path. Parameters ---------- path : str The input file path. key : str , optional The desired key for storing the node label. The default is "label". tolerance : str , optional The desired tolerance. The default is 0.0001. Returns ------- dict A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'. """ if not path: print("Graph.ByDGCNNPath - Error: the input path is not a valid path. Returning None.") return None try: file = open(path) except: print("Graph.ByDGCNNPath - Error: the DGCNN file is not a valid file. Returning None.") return None return Graph.ByDGCNNFile(file, key=key, tolerance=tolerance) @staticmethod def ByDGCNNString(string, key: str ="label", tolerance: float = 0.0001): """ Creates a graph from a DGCNN string. Parameters ---------- string : str The input string. key : str , optional The desired key for storing the node label. The default is "label". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- dict A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary import random def verticesByCoordinates(x_coords, y_coords): vertices = [] for i in range(len(x_coords)): vertices.append(Vertex.ByCoordinates(x_coords[i], y_coords[i], 0)) return vertices graphs = [] labels = [] lines = string.split("\n") n_graphs = int(lines[0]) index = 1 for i in range(n_graphs): edges = [] line = lines[index].split() n_nodes = int(line[0]) graph_label = int(line[1]) labels.append(graph_label) index+=1 x_coordinates = random.sample(range(0, n_nodes), n_nodes) y_coordinates = random.sample(range(0, n_nodes), n_nodes) vertices = verticesByCoordinates(x_coordinates, y_coordinates) for j in range(n_nodes): line = lines[index+j].split() node_label = int(line[0]) node_dict = Dictionary.ByKeysValues([key], [node_label]) Topology.SetDictionary(vertices[j], node_dict) for j in range(n_nodes): line = lines[index+j].split() sv = vertices[j] adj_vertices = line[2:] for adj_vertex in adj_vertices: ev = vertices[int(adj_vertex)] e = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance) edges.append(e) index+=n_nodes graphs.append(Graph.ByVerticesEdges(vertices, edges)) return {'graphs':graphs, 'labels':labels} @staticmethod def ByIFCFile(file, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5): """ Create a Graph from an IFC file. This code is partially based on code from Bruno Postle. Parameters ---------- file : file The input IFC file includeTypes : list , optional A list of IFC object types to include in the graph. The default is [] which means all object types are included. excludeTypes : list , optional A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded. includeRels : list , optional A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included. excludeRels : list , optional A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded. xMin : float, optional The desired minimum value to assign for a vertex's X coordinate. The default is -0.5. yMin : float, optional The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5. zMin : float, optional The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5. xMax : float, optional The desired maximum value to assign for a vertex's X coordinate. The default is 0.5. yMax : float, optional The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5. zMax : float, optional The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Topology import Topology from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Graph import Graph from topologicpy.Dictionary import Dictionary try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom except: print("Graph.ByIFCFile - Warning: Installing required ifcopenshell library.") try: os.system("pip install ifcopenshell") except: os.system("pip install ifcopenshell --user") try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom print("Graph.ByIFCFile - Warning: ifcopenshell library installed correctly.") except: warnings.warn("Graph.ByIFCFile - Error: Could not import ifcopenshell. Please try to install ifcopenshell manually. Returning None.") return None import random def vertexAtKeyValue(vertices, key, value): for v in vertices: d = Topology.Dictionary(v) d_value = Dictionary.ValueAtKey(d, key) if value == d_value: return v return None def IFCObjects(ifc_file, include=[], exclude=[]): include = [s.lower() for s in include] exclude = [s.lower() for s in exclude] all_objects = ifc_file.by_type('IfcProduct') return_objects = [] for obj in all_objects: is_a = obj.is_a().lower() if is_a in exclude: continue if is_a in include or len(include) == 0: return_objects.append(obj) return return_objects def IFCObjectTypes(ifc_file): products = IFCObjects(ifc_file) obj_types = [] for product in products: obj_types.append(product.is_a()) obj_types = list(set(obj_types)) obj_types.sort() return obj_types def IFCRelationshipTypes(ifc_file): rel_types = [ifc_rel.is_a() for ifc_rel in ifc_file.by_type("IfcRelationship")] rel_types = list(set(rel_types)) rel_types.sort() return rel_types def IFCRelationships(ifc_file, include=[], exclude=[]): include = [s.lower() for s in include] exclude = [s.lower() for s in exclude] rel_types = [ifc_rel.is_a() for ifc_rel in ifc_file.by_type("IfcRelationship")] rel_types = list(set(rel_types)) relationships = [] for ifc_rel in ifc_file.by_type("IfcRelationship"): rel_type = ifc_rel.is_a().lower() if rel_type in exclude: continue if rel_type in include or len(include) == 0: relationships.append(ifc_rel) return relationships def vertexByIFCObject(ifc_object, object_types, restrict=False): settings = ifcopenshell.geom.settings() settings.set(settings.USE_BREP_DATA,False) settings.set(settings.SEW_SHELLS,True) settings.set(settings.USE_WORLD_COORDS,True) try: shape = ifcopenshell.geom.create_shape(settings, ifc_object) except: shape = None if shape or restrict == False: #Only add vertices of entities that have 3D geometries. obj_id = ifc_object.id() psets = ifcopenshell.util.element.get_psets(ifc_object) obj_type = ifc_object.is_a() obj_type_id = object_types.index(obj_type) name = "Untitled" LongName = "Untitled" try: name = ifc_object.Name except: name = "Untitled" try: LongName = ifc_object.LongName except: LongName = name if name == None: name = "Untitled" if LongName == None: LongName = "Untitled" label = str(obj_id)+" "+LongName+" ("+obj_type+" "+str(obj_type_id)+")" try: grouped_verts = ifcopenshell.util.shape.get_vertices(shape.geometry) vertices = [Vertex.ByCoordinates(list(coords)) for coords in grouped_verts] centroid = Vertex.Centroid(vertices) except: x = random.uniform(xMin,xMax) y = random.uniform(yMin,yMax) z = random.uniform(zMin,zMax) centroid = Vertex.ByCoordinates(x, y, z) d = Dictionary.ByKeysValues(["id","psets", "type", "type_id", "name", "label"], [obj_id, psets, obj_type, obj_type_id, name, label]) centroid = Topology.SetDictionary(centroid, d) return centroid return None def edgesByIFCRelationships(ifc_relationships, ifc_types, vertices): tuples = [] edges = [] for ifc_rel in ifc_relationships: source = None destinations = [] if ifc_rel.is_a("IfcRelAggregates"): source = ifc_rel.RelatingObject destinations = ifc_rel.RelatedObjects if ifc_rel.is_a("IfcRelNests"): source = ifc_rel.RelatingObject destinations = ifc_rel.RelatedObjects if ifc_rel.is_a("IfcRelAssignsToGroup"): source = ifc_rel.RelatingGroup destinations = ifc_rel.RelatedObjects if ifc_rel.is_a("IfcRelConnectsPathElements"): source = ifc_rel.RelatingElement destinations = [ifc_rel.RelatedElement] if ifc_rel.is_a("IfcRelConnectsStructuralMember"): source = ifc_rel.RelatingStructuralMember destinations = [ifc_rel.RelatedStructuralConnection] if ifc_rel.is_a("IfcRelContainedInSpatialStructure"): source = ifc_rel.RelatingStructure destinations = ifc_rel.RelatedElements if ifc_rel.is_a("IfcRelFillsElement"): source = ifc_rel.RelatingOpeningElement destinations = [ifc_rel.RelatedBuildingElement] if ifc_rel.is_a("IfcRelSpaceBoundary"): source = ifc_rel.RelatingSpace destinations = [ifc_rel.RelatedBuildingElement] if ifc_rel.is_a("IfcRelVoidsElement"): source = ifc_rel.RelatingBuildingElement destinations = [ifc_rel.RelatedOpeningElement] if source: sv = vertexAtKeyValue(vertices, key="id", value=source.id()) if sv: si = Vertex.Index(sv, vertices) for destination in destinations: if destination == None: continue ev = vertexAtKeyValue(vertices, key="id", value=destination.id()) if ev: ei = Vertex.Index(ev, vertices) if not([si,ei] in tuples or [ei,si] in tuples): tuples.append([si,ei]) e = Edge.ByVertices([sv,ev]) d = Dictionary.ByKeysValues(["id", "name", "type"], [ifc_rel.id(), ifc_rel.Name, ifc_rel.is_a()]) e = Topology.SetDictionary(e, d) edges.append(e) return edges ifc_types = IFCObjectTypes(file) ifc_objects = IFCObjects(file, include=includeTypes, exclude=excludeTypes) vertices = [] for ifc_object in ifc_objects: v = vertexByIFCObject(ifc_object, ifc_types) if v: vertices.append(v) if len(vertices) > 0: ifc_relationships = IFCRelationships(file, include=includeRels, exclude=excludeRels) edges = edgesByIFCRelationships(ifc_relationships, ifc_types, vertices) g = Graph.ByVerticesEdges(vertices, edges) else: g = None return g @staticmethod def ByIFCPath(path, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5): """ Create a Graph from an IFC path. This code is partially based on code from Bruno Postle. Parameters ---------- path : str The input IFC file path. includeTypes : list , optional A list of IFC object types to include in the graph. The default is [] which means all object types are included. excludeTypes : list , optional A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded. includeRels : list , optional A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included. excludeRels : list , optional A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded. xMin : float, optional The desired minimum value to assign for a vertex's X coordinate. The default is -0.5. yMin : float, optional The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5. zMin : float, optional The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5. xMax : float, optional The desired maximum value to assign for a vertex's X coordinate. The default is 0.5. yMax : float, optional The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5. zMax : float, optional The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5. Returns ------- topologic_core.Graph The created graph. """ try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom except: print("Graph.ByIFCPath - Warning: Installing required ifcopenshell library.") try: os.system("pip install ifcopenshell") except: os.system("pip install ifcopenshell --user") try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom print("Graph.ByIFCPath - Warning: ifcopenshell library installed correctly.") except: warnings.warn("Graph.ByIFCPath - Error: Could not import ifcopenshell. Please try to install ifcopenshell manually. Returning None.") return None if not path: print("Graph.ByIFCPath - Error: the input path is not a valid path. Returning None.") return None ifc_file = ifcopenshell.open(path) if not ifc_file: print("Graph.ByIFCPath - Error: Could not open the IFC file. Returning None.") return None return Graph.ByIFCFile(ifc_file, includeTypes=includeTypes, excludeTypes=excludeTypes, includeRels=includeRels, excludeRels=excludeRels, xMin=xMin, yMin=yMin, zMin=zMin, xMax=xMax, yMax=yMax, zMax=zMax) @staticmethod def ByMeshData(vertices, edges, vertexDictionaries=None, edgeDictionaries=None, tolerance=0.0001): """ Creates a graph from the input mesh data Parameters ---------- vertices : list The list of [x, y, z] coordinates of the vertices/ edges : list the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j]. vertexDictionaries : list , optional The python dictionaries of the vertices (in the same order as the list of vertices). edgeDictionaries : list , optional The python dictionaries of the edges (in the same order as the list of edges). tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The created graph """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology g_vertices = [] for i, v in enumerate(vertices): g_v = Vertex.ByCoordinates(v[0], v[1], v[2]) if not vertexDictionaries == None: if isinstance(vertexDictionaries[i], dict): d = Dictionary.ByPythonDictionary(vertexDictionaries[i]) else: d = vertexDictionaries[i] if not d == None: if len(Dictionary.Keys(d)) > 0: g_v = Topology.SetDictionary(g_v, d) g_vertices.append(g_v) g_edges = [] for i, e in enumerate(edges): sv = g_vertices[e[0]] ev = g_vertices[e[1]] g_e = Edge.ByVertices([sv, ev], tolerance=tolerance) if not edgeDictionaries == None: if isinstance(edgeDictionaries[i], dict): d = Dictionary.ByPythonDictionary(edgeDictionaries[i]) else: d = edgeDictionaries[i] if not d == None: if len(Dictionary.Keys(d)) > 0: g_e = Topology.SetDictionary(g_e, d) g_edges.append(g_e) return Graph.ByVerticesEdges(g_vertices, g_edges) @staticmethod def ByTopology(topology, direct=True, directApertures=False, viaSharedTopologies=False, viaSharedApertures=False, toExteriorTopologies=False, toExteriorApertures=False, toContents=False, toOutposts=False, idKey="TOPOLOGIC_ID", outpostsKey="outposts", useInternalVertex=True, storeBREP=False, tolerance=0.0001): """ Creates a graph.See https://en.wikipedia.org/wiki/Graph_(discrete_mathematics). Parameters ---------- topology : topologic_core.Topology The input topology. direct : bool , optional If set to True, connect the subtopologies directly with a single edge. The default is True. directApertures : bool , optional If set to True, connect the subtopologies directly with a single edge if they share one or more apertures. The default is False. viaSharedTopologies : bool , optional If set to True, connect the subtopologies via their shared topologies. The default is False. viaSharedApertures : bool , optional If set to True, connect the subtopologies via their shared apertures. The default is False. toExteriorTopologies : bool , optional If set to True, connect the subtopologies to their exterior topologies. The default is False. toExteriorApertures : bool , optional If set to True, connect the subtopologies to their exterior apertures. The default is False. toContents : bool , optional If set to True, connect the subtopologies to their contents. The default is False. toOutposts : bool , optional If set to True, connect the topology to the list specified in its outposts. The default is False. idKey : str , optional The key to use to find outpost by ID. It is case insensitive. The default is "TOPOLOGIC_ID". outpostsKey : str , optional The key to use to find the list of outposts. It is case insensitive. The default is "outposts". useInternalVertex : bool , optional If set to True, use an internal vertex to represent the subtopology. Otherwise, use its centroid. The default is False. storeBREP : bool , optional If set to True, store the BRep of the subtopology in its representative vertex. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Dictionary import Dictionary from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology from topologicpy.Aperture import Aperture def mergeDictionaries(sources): if isinstance(sources, list) == False: sources = [sources] sinkKeys = [] sinkValues = [] d = sources[0].GetDictionary() if d != None: stlKeys = d.Keys() if len(stlKeys) > 0: sinkKeys = d.Keys() sinkValues = Dictionary.Values(d) for i in range(1,len(sources)): d = sources[i].GetDictionary() if d == None: continue stlKeys = d.Keys() if len(stlKeys) > 0: sourceKeys = d.Keys() for aSourceKey in sourceKeys: if aSourceKey not in sinkKeys: sinkKeys.append(aSourceKey) sinkValues.append("") for i in range(len(sourceKeys)): index = sinkKeys.index(sourceKeys[i]) sourceValue = Dictionary.ValueAtKey(d, sourceKeys[i]) if sourceValue != None: if sinkValues[index] != "": if isinstance(sinkValues[index], list): sinkValues[index].append(sourceValue) else: sinkValues[index] = [sinkValues[index], sourceValue] else: sinkValues[index] = sourceValue if len(sinkKeys) > 0 and len(sinkValues) > 0: return Dictionary.ByKeysValues(sinkKeys, sinkValues) return None def mergeDictionaries2(sources): if isinstance(sources, list) == False: sources = [sources] sinkKeys = [] sinkValues = [] d = sources[0] if d != None: stlKeys = d.Keys() if len(stlKeys) > 0: sinkKeys = d.Keys() sinkValues = Dictionary.Values(d) for i in range(1,len(sources)): d = sources[i] if d == None: continue stlKeys = d.Keys() if len(stlKeys) > 0: sourceKeys = d.Keys() for aSourceKey in sourceKeys: if aSourceKey not in sinkKeys: sinkKeys.append(aSourceKey) sinkValues.append("") for i in range(len(sourceKeys)): index = sinkKeys.index(sourceKeys[i]) sourceValue = Dictionary.ValueAtKey(d, sourceKeys[i]) if sourceValue != None: if sinkValues[index] != "": if isinstance(sinkValues[index], list): sinkValues[index].append(sourceValue) else: sinkValues[index] = [sinkValues[index], sourceValue] else: sinkValues[index] = sourceValue if len(sinkKeys) > 0 and len(sinkValues) > 0: return Dictionary.ByKeysValues(sinkKeys, sinkValues) return None def outpostsByID(topologies, ids, idKey="TOPOLOGIC_ID"): returnList = [] idList = [] for t in topologies: d = Topology.Dictionary(t) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == idKey.lower(): k = key if k: id = Dictionary.ValueAtKey(d, k) else: id = "" idList.append(id) for id in ids: try: index = idList.index(id) except: index = None if index: returnList.append(topologies[index]) return returnList def processCellComplex(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item edges = [] vertices = [] cellmat = [] if direct == True: cells = [] _ = topology.Cells(None, cells) # Create a matrix of zeroes for i in range(len(cells)): cellRow = [] for j in range(len(cells)): cellRow.append(0) cellmat.append(cellRow) for i in range(len(cells)): for j in range(len(cells)): if (i != j) and cellmat[i][j] == 0: cellmat[i][j] = 1 cellmat[j][i] = 1 sharedt = Topology.SharedFaces(cells[i], cells[j]) if len(sharedt) > 0: if useInternalVertex == True: v1 = Topology.InternalVertex(cells[i], tolerance=tolerance) v2 = Topology.InternalVertex(cells[j], tolerance=tolerance) else: v1 = cells[i].CenterOfMass() v2 = cells[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(sharedt) if not mDict == None: keys = (Dictionary.Keys(mDict) or [])+["relationship"] values = (Dictionary.Values(mDict) or [])+["Direct"] else: keys = ["relationship"] values = ["Direct"] mDict = Dictionary.ByKeysValues(keys, values) if mDict: e.SetDictionary(mDict) edges.append(e) if directApertures == True: cellmat = [] cells = [] _ = topology.Cells(None, cells) # Create a matrix of zeroes for i in range(len(cells)): cellRow = [] for j in range(len(cells)): cellRow.append(0) cellmat.append(cellRow) for i in range(len(cells)): for j in range(len(cells)): if (i != j) and cellmat[i][j] == 0: cellmat[i][j] = 1 cellmat[j][i] = 1 sharedt = Topology.SharedFaces(cells[i], cells[j]) if len(sharedt) > 0: apertureExists = False for x in sharedt: apList = [] _ = x.Apertures(apList) if len(apList) > 0: apTopList = [] for ap in apList: apTopList.append(ap.Topology()) apertureExists = True break if apertureExists: if useInternalVertex == True: v1 = Topology.InternalVertex(cells[i], tolerance=tolerance) v2 = Topology.InternalVertex(cells[j], tolerance=tolerance) else: v1 = cells[i].CenterOfMass() v2 = cells[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(apTopList) if mDict: e.SetDictionary(mDict) edges.append(e) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance=tolerance) vcc = Topology.InternalVertex(topology, tolerance=tolerance) else: vop = Topology.CenterOfMass(outpost) vcc = Topology.CenterOfMass(topology) d1 = Topology.Dictionary(vcc) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vcc.SetDictionary(d3) else: _ = vcc.SetDictionary(d1) vertices.append(vcc) tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) cells = [] _ = topology.Cells(None, cells) if any([viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents]): for aCell in cells: if useInternalVertex == True: vCell = Topology.InternalVertex(aCell, tolerance=tolerance) else: vCell = aCell.CenterOfMass() d1 = aCell.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aCell), Topology.Type(aCell), Topology.TypeAsString(aCell)]) d3 = mergeDictionaries2([d1, d2]) _ = vCell.SetDictionary(d3) else: _ = vCell.SetDictionary(d1) vertices.append(vCell) faces = [] _ = aCell.Faces(None, faces) sharedTopologies = [] exteriorTopologies = [] sharedApertures = [] exteriorApertures = [] contents = [] _ = aCell.Contents(contents) for aFace in faces: cells1 = [] _ = aFace.Cells(topology, cells1) if len(cells1) > 1: sharedTopologies.append(aFace) apertures = [] _ = aFace.Apertures(apertures) for anAperture in apertures: sharedApertures.append(anAperture) else: exteriorTopologies.append(aFace) apertures = [] _ = aFace.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if viaSharedTopologies: for sharedTopology in sharedTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(sharedTopology, tolerance) else: vst = sharedTopology.CenterOfMass() d1 = sharedTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = sharedTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() d1 = content.GetDictionary() vst2 = Vertex.ByCoordinates(vst2.X(), vst2.Y(), vst2.Z()) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if viaSharedApertures: for sharedAperture in sharedApertures: sharedAp = sharedAperture.Topology() if useInternalVertex == True: vsa = Topology.InternalVertex(sharedAp, tolerance) else: vsa = sharedAp.CenterOfMass() d1 = sharedAp.GetDictionary() vsa = Vertex.ByCoordinates(vsa.X()+(tolerance*100), vsa.Y()+(tolerance*100), vsa.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)]) d3 = mergeDictionaries2([d1, d2]) _ = vsa.SetDictionary(d3) else: _ = vsa.SetDictionary(d1) vertices.append(vsa) tempe = Edge.ByStartVertexEndVertex(vCell, vsa, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vet = Topology.InternalVertex(exteriorTopology, tolerance) else: vet = exteriorTopology.CenterOfMass() _ = vet.SetDictionary(exteriorTopology.GetDictionary()) d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vet.SetDictionary(d3) else: _ = vet.SetDictionary(d1) vertices.append(vet) tempe = Edge.ByStartVertexEndVertex(vCell, vet, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() d1 = content.GetDictionary() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vet, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vea = Topology.InternalVertex(exTop, tolerance) else: vea = exTop.CenterOfMass() d1 = exTop.GetDictionary() vea = Vertex.ByCoordinates(vea.X()+(tolerance*100), vea.Y()+(tolerance*100), vea.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vea.SetDictionary(d3) else: _ = vea.SetDictionary(d1) vertices.append(vea) tempe = Edge.ByStartVertexEndVertex(vCell, vea, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = aCell.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vcn = Topology.InternalVertex(content, tolerance) else: vcn = content.CenterOfMass() vcn = Vertex.ByCoordinates(vcn.X()+(tolerance*100), vcn.Y()+(tolerance*100), vcn.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vcn.SetDictionary(d3) else: _ = vcn.SetDictionary(d1) vertices.append(vcn) tempe = Edge.ByStartVertexEndVertex(vCell, vcn, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) for aCell in cells: if useInternalVertex == True: vCell = Topology.InternalVertex(aCell, tolerance=tolerance) else: vCell = aCell.CenterOfMass() d1 = aCell.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aCell), Topology.Type(aCell), Topology.TypeAsString(aCell)]) d3 = mergeDictionaries2([d1, d2]) _ = vCell.SetDictionary(d3) else: _ = vCell.SetDictionary(d1) vertices.append(vCell) return [vertices,edges] def processCell(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item vertices = [] edges = [] if useInternalVertex == True: vCell = Topology.InternalVertex(topology, tolerance=tolerance) else: vCell = topology.CenterOfMass() d1 = topology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vCell.SetDictionary(d3) else: _ = vCell.SetDictionary(d1) vertices.append(vCell) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(vCell, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if any([toExteriorTopologies, toExteriorApertures, toContents]): faces = Topology.Faces(topology) exteriorTopologies = [] exteriorApertures = [] for aFace in faces: exteriorTopologies.append(aFace) apertures = Topology.Apertures(aFace) for anAperture in apertures: exteriorApertures.append(anAperture) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = topology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processShell(item): from topologicpy.Face import Face topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None edges = [] vertices = [] facemat = [] if direct == True: topFaces = [] _ = topology.Faces(None, topFaces) # Create a matrix of zeroes for i in range(len(topFaces)): faceRow = [] for j in range(len(topFaces)): faceRow.append(0) facemat.append(faceRow) for i in range(len(topFaces)): for j in range(len(topFaces)): if (i != j) and facemat[i][j] == 0: facemat[i][j] = 1 facemat[j][i] = 1 sharedt = Topology.SharedEdges(topFaces[i], topFaces[j]) if len(sharedt) > 0: if useInternalVertex == True: v1 = Topology.InternalVertex(topFaces[i], tolerance=tolerance) v2 = Topology.InternalVertex(topFaces[j], tolerance=tolerance) else: v1 = topFaces[i].CenterOfMass() v2 = topFaces[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(sharedt) if not mDict == None: keys = (Dictionary.Keys(mDict) or [])+["relationship"] values = (Dictionary.Values(mDict) or [])+["Direct"] else: keys = ["relationship"] values = ["Direct"] mDict = Dictionary.ByKeysValues(keys, values) if mDict: e.SetDictionary(mDict) edges.append(e) if directApertures == True: facemat = [] topFaces = [] _ = topology.Faces(None, topFaces) # Create a matrix of zeroes for i in range(len(topFaces)): faceRow = [] for j in range(len(topFaces)): faceRow.append(0) facemat.append(faceRow) for i in range(len(topFaces)): for j in range(len(topFaces)): if (i != j) and facemat[i][j] == 0: facemat[i][j] = 1 facemat[j][i] = 1 sharedt = Topology.SharedEdges(topFaces[i], topFaces[j]) if len(sharedt) > 0: apertureExists = False for x in sharedt: apList = [] _ = x.Apertures(apList) if len(apList) > 0: apertureExists = True break if apertureExists: apTopList = [] for ap in apList: apTopList.append(ap.Topology()) if useInternalVertex == True: v1 = Topology.InternalVertex(topFaces[i], tolerance=tolerance) v2 = Topology.InternalVertex(topFaces[j], tolerance=tolerance) else: v1 = topFaces[i].CenterOfMass() v2 = topFaces[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(apTopList) if mDict: e.SetDictionary(mDict) edges.append(e) topFaces = [] _ = topology.Faces(None, topFaces) if any([viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents == True]): for aFace in topFaces: if useInternalVertex == True: vFace = Topology.InternalVertex(aFace, tolerance=tolerance) else: vFace = aFace.CenterOfMass() _ = vFace.SetDictionary(aFace.GetDictionary()) vertices.append(vFace) fEdges = [] _ = aFace.Edges(None, fEdges) sharedTopologies = [] exteriorTopologies = [] sharedApertures = [] exteriorApertures = [] for anEdge in fEdges: faces = [] _ = anEdge.Faces(topology, faces) if len(faces) > 1: sharedTopologies.append(anEdge) apertures = [] _ = anEdge.Apertures(apertures) for anAperture in apertures: sharedApertures.append(anAperture) else: exteriorTopologies.append(anEdge) apertures = [] _ = anEdge.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if viaSharedTopologies: for sharedTopology in sharedTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(sharedTopology, tolerance) else: vst = sharedTopology.CenterOfMass() d1 = sharedTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = sharedTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if viaSharedApertures: for sharedAperture in sharedApertures: sharedAp = Aperture.Topology(sharedAperture) if useInternalVertex == True: vst = Topology.InternalVertex(sharedAp, tolerance) else: vst = sharedAp.CenterOfMass() d1 = sharedAp.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = aFace.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) for aFace in topFaces: if useInternalVertex == True: vFace = Topology.InternalVertex(aFace, tolerance) else: vFace = aFace.CenterOfMass() d1 = aFace.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aFace), Topology.Type(aFace), Topology.TypeAsString(aFace)]) d3 = mergeDictionaries2([d1, d2]) _ = vFace.SetDictionary(d3) else: _ = vFace.SetDictionary(d1) vertices.append(vFace) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) vcc = Topology.InternalVertex(topology, tolerance) else: vop = Topology.CenterOfMass(outpost) vcc = Topology.CenterOfMass(topology) d1 = Topology.Dictionary(vcc) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vcc.SetDictionary(d3) else: _ = vcc.SetDictionary(d1) vertices.append(vcc) tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processFace(item): from topologicpy.Face import Face topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None vertices = [] edges = [] if useInternalVertex == True: vFace = Topology.InternalVertex(topology, tolerance=tolerance) else: vFace = topology.CenterOfMass() d1 = topology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vFace.SetDictionary(d3) else: _ = vFace.SetDictionary(d1) vertices.append(vFace) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(vFace, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True): fEdges = [] _ = topology.Edges(None, fEdges) exteriorTopologies = [] exteriorApertures = [] for anEdge in fEdges: exteriorTopologies.append(anEdge) apertures = [] _ = anEdge.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = topology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processWire(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None edges = [] vertices = [] edgemat = [] if direct == True: topEdges = [] _ = topology.Edges(None, topEdges) # Create a matrix of zeroes for i in range(len(topEdges)): edgeRow = [] for j in range(len(topEdges)): edgeRow.append(0) edgemat.append(edgeRow) for i in range(len(topEdges)): for j in range(len(topEdges)): if (i != j) and edgemat[i][j] == 0: edgemat[i][j] = 1 edgemat[j][i] = 1 sharedt = Topology.SharedVertices(topEdges[i], topEdges[j]) if len(sharedt) > 0: try: v1 = Edge.VertexByParameter(topEdges[i], 0.5) except: v1 = topEdges[j].CenterOfMass() try: v2 = Edge.VertexByParameter(topEdges[j], 0.5) except: v2 = topEdges[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(sharedt) if not mDict == None: keys = (Dictionary.Keys(mDict) or [])+["relationship"] values = (Dictionary.Values(mDict) or [])+["Direct"] else: keys = ["relationship"] values = ["Direct"] mDict = Dictionary.ByKeysValues(keys, values) if mDict: e.SetDictionary(mDict) edges.append(e) if directApertures == True: edgemat = [] topEdges = [] _ = topology.Edges(None, topEdges) # Create a matrix of zeroes for i in range(len(topEdges)): edgeRow = [] for j in range(len(topEdges)): edgeRow.append(0) edgemat.append(edgeRow) for i in range(len(topEdges)): for j in range(len(topEdges)): if (i != j) and edgemat[i][j] == 0: edgemat[i][j] = 1 edgemat[j][i] = 1 sharedt = Topology.SharedVertices(topEdges[i], topEdges[j]) if len(sharedt) > 0: apertureExists = False for x in sharedt: apList = [] _ = x.Apertures(apList) if len(apList) > 0: apertureExists = True break if apertureExists: try: v1 = Edge.VertexByParameter(topEdges[i], 0.5) except: v1 = topEdges[j].CenterOfMass() try: v2 = Edge.VertexByParameter(topEdges[j], 0.5) except: v2 = topEdges[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) apTopologies = [] for ap in apList: apTopologies.append(ap.Topology()) mDict = mergeDictionaries(apTopologies) if mDict: e.SetDictionary(mDict) edges.append(e) topEdges = [] _ = topology.Edges(None, topEdges) if (viaSharedTopologies == True) or (viaSharedApertures == True) or (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True): for anEdge in topEdges: try: vEdge = Edge.VertexByParameter(anEdge, 0.5) except: vEdge = anEdge.CenterOfMass() d1 = anEdge.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(anEdge), Topology.Type(anEdge), Topology.TypeAsString(anEdge)]) d3 = mergeDictionaries2([d1, d2]) _ = vEdge.SetDictionary(d3) else: _ = vEdge.SetDictionary(d1) vertices.append(vEdge) eVertices = [] _ = anEdge.Vertices(None, eVertices) sharedTopologies = [] exteriorTopologies = [] sharedApertures = [] exteriorApertures = [] contents = [] _ = anEdge.Contents(contents) for aVertex in eVertices: tempEdges = [] _ = aVertex.Edges(topology, tempEdges) if len(tempEdges) > 1: sharedTopologies.append(aVertex) apertures = [] _ = aVertex.Apertures(apertures) for anAperture in apertures: sharedApertures.append(anAperture) else: exteriorTopologies.append(aVertex) apertures = [] _ = aVertex.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if viaSharedTopologies: for sharedTopology in sharedTopologies: vst = sharedTopology.CenterOfMass() d1 = sharedTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = sharedTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if viaSharedApertures: for sharedAperture in sharedApertures: sharedAp = sharedAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(sharedAp, tolerance) else: vst = sharedAp.CenterOfMass() d1 = sharedAp.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: vst = exteriorTopology vertices.append(exteriorTopology) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = vst.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = anEdge.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) for anEdge in topEdges: try: vEdge = Edge.VertexByParameter(anEdge, 0.5) except: vEdge = anEdge.CenterOfMass() d1 = anEdge.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(anEdge), Topology.Type(anEdge), Topology.TypeAsString(anEdge)]) d3 = mergeDictionaries2([d1, d2]) _ = vEdge.SetDictionary(d3) else: _ = vEdge.SetDictionary(d1) vertices.append(vEdge) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) vcc = Topology.InternalVertex(topology, tolerance) else: vop = Topology.CenterOfMass(outpost) vcc = Topology.CenterOfMass(topology) d1 = Topology.Dictionary(vcc) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vcc.SetDictionary(d3) else: _ = vcc.SetDictionary(d1) vertices.append(vcc) tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processEdge(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None vertices = [] edges = [] if useInternalVertex == True: try: vEdge = Edge.VertexByParameter(topology, 0.5) except: vEdge = topology.CenterOfMass() else: vEdge = topology.CenterOfMass() d1 = vEdge.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vEdge.SetDictionary(d3) else: _ = vEdge.SetDictionary(topology.GetDictionary()) vertices.append(vEdge) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(vEdge, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True): eVertices = [] _ = topology.Vertices(None, eVertices) exteriorTopologies = [] exteriorApertures = [] for aVertex in eVertices: exteriorTopologies.append(aVertex) apertures = [] _ = aVertex.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = vst.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) _ = vst.SetDictionary(exTop.GetDictionary()) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processVertex(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item vertices = [topology] edges = [] if toContents: contents = [] _ = topology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() d1 = content.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(topology, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(topology, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] if not Topology.IsInstance(topology, "Topology"): print("Graph.ByTopology - Error: The input topology is not a valid topology. Returning None.") return None graph = None item = [topology, None, None, None, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, None, useInternalVertex, storeBREP, tolerance] vertices = [] edges = [] if Topology.IsInstance(topology, "CellComplex"): vertices, edges = processCellComplex(item) elif Topology.IsInstance(topology, "Cell"): vertices, edges = processCell(item) elif Topology.IsInstance(topology, "Shell"): vertices, edges = processShell(item) elif Topology.IsInstance(topology, "Face"): vertices, edges = processFace(item) elif Topology.IsInstance(topology, "Wire"): vertices, edges = processWire(item) elif Topology.IsInstance(topology, "Edge"): vertices, edges = processEdge(item) elif Topology.IsInstance(topology, "Vertex"): vertices, edges = processVertex(item) elif Topology.IsInstance(topology, "Cluster"): c_cellComplexes = Topology.CellComplexes(topology) c_cells = Cluster.FreeCells(topology, tolerance=tolerance) c_shells = Cluster.FreeShells(topology, tolerance=tolerance) c_faces = Cluster.FreeFaces(topology, tolerance=tolerance) c_wires = Cluster.FreeWires(topology, tolerance=tolerance) c_edges = Cluster.FreeEdges(topology, tolerance=tolerance) c_vertices = Cluster.FreeVertices(topology, tolerance=tolerance) others = c_cellComplexes+c_cells+c_shells+c_faces+c_wires+c_edges+c_vertices parameters = [others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance] for t in c_cellComplexes: v, e = processCellComplex([t]+parameters) vertices += v edges += e for t in c_cells: v, e = processCell([t]+parameters) vertices += v edges += e for t in c_shells: v, e = processShell([t]+parameters) vertices += v edges += e for t in c_faces: v, e = processFace([t]+parameters) vertices += v edges += e for t in c_wires: v, e = processWire([t]+parameters) vertices += v edges += e for t in c_edges: v, e = processEdge([t]+parameters) vertices += v edges += e for t in c_vertices: v, e = processVertex([t]+parameters) vertices += v edges += e else: return None return Graph.ByVerticesEdges(vertices, edges) @staticmethod def ByVerticesEdges(vertices, edges): """ Creates a graph from the input list of vertices and edges. Parameters ---------- vertices : list The input list of vertices. edges : list The input list of edges. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Topology import Topology if not isinstance(vertices, list): print("Graph.ByVerticesEdges - Error: The input list of vertices is not a valid list. Returning None.") return None if not isinstance(edges, list): print("Graph.ByVerticesEdges - Error: The input list of edges is not a valid list. Returning None.") return None vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] edges = [e for e in edges if Topology.IsInstance(e, "Edge")] return topologic.Graph.ByVerticesEdges(vertices, edges) # Hook to Core @staticmethod def ChromaticNumber(graph, maxColors: int = 3, silent: bool = False): """ Returns the chromatic number of the input graph. See https://en.wikipedia.org/wiki/Graph_coloring. Parameters ---------- graph : topologic_core.Graph The input graph. maxColors : int , optional The desired maximum number of colors to test against. The default is 3. silent : bool , optional If set to False, error and warning messages are printed. Otherwise, they are not. The default is False. Returns ------- int The chromatic number of the input graph. """ # This is based on code from https://www.geeksforgeeks.org/graph-coloring-applications/ from topologicpy.Topology import Topology def is_safe(graph, v, color, c): for i in range(len(graph)): if graph[v][i] == 1 and color[i] == c: return False return True def graph_coloring(graph, m, color, v): V = len(graph) if v == V: return True for c in range(1, m + 1): if is_safe(graph, v, color, c): color[v] = c if graph_coloring(graph, m, color, v + 1): return True color[v] = 0 return False def chromatic_number(graph): V = len(graph) color = [0] * V m = 1 while True: if graph_coloring(graph, m, color, 0): return m m += 1 if not Topology.IsInstance(graph, "Graph"): if not silent: print("Graph.ChromaticNumber - Error: The input graph parameter is not a valid graph. Returning None.") return None if maxColors < 1: if not silent: print("Graph.ChromaticNumber - Error: The input maxColors parameter is not a valid positive number. Returning None.") return None adj_matrix = Graph.AdjacencyMatrix(graph) return chromatic_number(adj_matrix) @staticmethod def Color(graph, oldKey: str = "color", newKey: str = "color", maxColors: int = None, tolerance: float = 0.0001): """ Colors the input vertices within the input graph. The saved value is an integer rather than an actual color. See Color.ByValueInRange to convert to an actual color. Any vertices that have been pre-colored will not be affected. See https://en.wikipedia.org/wiki/Graph_coloring. Parameters ---------- graph : topologic_core.Graph The input graph. oldKey : str , optional The existing dictionary key to use to read any pre-existing color information. The default is "color". newKey : str , optional The new dictionary key to use to write out new color information. The default is "color". maxColors : int , optional The desired maximum number of colors to use. If set to None, the chromatic number of the graph is used. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph, but with its vertices colored. """ from topologicpy.Vertex import Vertex from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import math def is_safe(v, graph, colors, c): # Check if the color 'c' is safe for the vertex 'v' for i in range(len(graph)): if graph[v][i] and c == colors[i]: return False return True def graph_coloring_util(graph, m, colors, v): # Base case: If all vertices are assigned a color, return true if v == len(graph): return True # Try different colors for the current vertex 'v' for c in range(1, m + 1): # Check if assignment of color 'c' to 'v' is fine if is_safe(v, graph, colors, c): colors[v] = c # Recur to assign colors to the rest of the vertices if graph_coloring_util(graph, m, colors, v + 1): return True # If assigning color 'c' doesn't lead to a solution, remove it colors[v] = 0 # If no color can be assigned to this vertex, return false return False def graph_coloring(graph, m, colors): # Call graph_coloring_util() for vertex 0 if not graph_coloring_util(graph, m, colors, 0): return None return [x-1 for x in colors] if not Topology.IsInstance(graph, "Graph"): print("Graph.Color - Error: The input graph is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) adj_mat = Graph.AdjacencyMatrix(graph) # Isolate vertices that have pre-existing colors as they shouldn't affect graph coloring. for i, v in enumerate(vertices): d = Topology.Dictionary(v) c = Dictionary.ValueAtKey(d, oldKey) if not c == None: adj_mat[i] = [0] * len(vertices) for j in range(len(adj_mat)): row = adj_mat[j] row[i] = 0 temp_graph = Graph.ByAdjacencyMatrix(adj_mat) # If the maximum number of colors are not provided, compute it using the graph's chromatic number. if maxColors == None: maxColors = Graph.ChromaticNumber(temp_graph) colors = [0] * len(vertices) colors = graph_coloring(adj_mat, maxColors, colors) for i, v in enumerate(vertices): d = Topology.Dictionary(v) d = Dictionary.SetValueAtKey(d, newKey, colors[i]) v = Topology.SetDictionary(v, d) return graph @staticmethod def ContractEdge(graph, edge, vertex=None, tolerance=0.0001): """ Contracts the input edge in the input graph into a single vertex. Please note that the dictionary of the edge is transferred to the vertex that replaces it. See https://en.wikipedia.org/wiki/Edge_contraction Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input graph edge that needs to be contracted. vertex : topollogic.Vertex , optional The vertex to replace the contracted edge. If set to None, the centroid of the edge is chosen. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph, but with input edge contracted into a single vertex. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary def OppositeVertex(edge, vertex, tolerance=0.0001): sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) d1 = Vertex.Distance(vertex, sv) d2 = Vertex.Distance(vertex, ev) if d1 < d2: return [ev, 1] return [sv, 0] if not Topology.IsInstance(graph, "Graph"): print("Graph.ContractEdge - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.ContractEdge - Error: The input edge parameter is not a valid edge. Returning None.") return None if vertex == None: vertex = Topology.Centroid(edge) sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) vd = Topology.Dictionary(vertex) sd = Topology.Dictionary(sv) dictionaries = [] keys = Dictionary.Keys(vd) if isinstance(keys, list): if len(keys) > 0: dictionaries.append(vd) keys = Dictionary.Keys(sd) if isinstance(keys, list): if len(keys) > 0: dictionaries.append(sd) ed = Topology.Dictionary(ev) keys = Dictionary.Keys(ed) if isinstance(keys, list): if len(keys) > 0: dictionaries.append(ed) if len(dictionaries) == 1: vertex = Topology.SetDictionary(vertex, dictionaries[0]) elif len(dictionaries) > 1: cd = Dictionary.ByMergedDictionaries(dictionaries) vertex = Topology.SetDictionary(vertex, cd) graph = Graph.RemoveEdge(graph, edge, tolerance=tolerance) graph = Graph.AddVertex(graph, vertex, tolerance=tolerance) adj_edges_sv = Graph.Edges(graph, [sv]) adj_edges_ev = Graph.Edges(graph, [ev]) new_edges = [] for adj_edge_sv in adj_edges_sv: ov, flag = OppositeVertex(adj_edge_sv, sv) if flag == 0: new_edge = Edge.ByVertices([ov, vertex]) else: new_edge = Edge.ByVertices([vertex, ov]) d = Topology.Dictionary(adj_edge_sv) keys = Dictionary.Keys(d) if isinstance(keys, list): if len(keys) > 0: new_edge = Topology.SetDictionary(new_edge, d) new_edges.append(new_edge) for adj_edge_ev in adj_edges_ev: ov, flag = OppositeVertex(adj_edge_ev, ev) if flag == 0: new_edge = Edge.ByVertices([ov, vertex]) else: new_edge = Edge.ByVertices([vertex, ov]) d = Topology.Dictionary(adj_edge_ev) keys = Dictionary.Keys(d) if isinstance(keys, list): if len(keys) > 0: new_edge = Topology.SetDictionary(new_edge, d) new_edges.append(new_edge) for new_edge in new_edges: graph = Graph.AddEdge(graph, new_edge, transferVertexDictionaries=True, transferEdgeDictionaries=True, tolerance=tolerance) graph = Graph.RemoveVertex(graph,sv, tolerance=tolerance) graph = Graph.RemoveVertex(graph,ev, tolerance=tolerance) return graph @staticmethod def ClosenessCentrality(graph, vertices=None, tolerance = 0.0001): """ Return the closeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the closeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Closeness_centrality. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.") return None graphVertices = Graph.Vertices(graph) if not isinstance(vertices, list): vertices = graphVertices else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.ClosenessCentrality - Error: The input list of vertices does not contain any valid vertices. Returning None.") return None n = len(graphVertices) returnList = [] try: for va in tqdm(vertices, desc="Computing Closeness Centrality", leave=False): top_dist = 0 for vb in graphVertices: if Topology.IsSame(va, vb): d = 0 else: d = Graph.TopologicalDistance(graph, va, vb, tolerance) top_dist += d if top_dist == 0: returnList.append(0) else: returnList.append((n-1)/top_dist) except: print("Graph.ClosenessCentrality - Warning: Could not use tqdm.") for va in vertices: top_dist = 0 for vb in graphVertices: if Topology.IsSame(va, vb): d = 0 else: d = Graph.TopologicalDistance(graph, va, vb, tolerance) top_dist += d if top_dist == 0: returnList.append(0) else: returnList.append((n-1)/top_dist) return returnList @staticmethod def Connect(graph, verticesA, verticesB, tolerance=0.0001): """ Connects the two lists of input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. verticesA : list The first list of input vertices. verticesB : topologic_core.Vertex The second list of input vertices. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the connected input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Connect - Error: The input graph is not a valid graph. Returning None.") return None if not isinstance(verticesA, list): print("Graph.Connect - Error: The input list of verticesA is not a valid list. Returning None.") return None if not isinstance(verticesB, list): print("Graph.Connect - Error: The input list of verticesB is not a valid list. Returning None.") return None verticesA = [v for v in verticesA if Topology.IsInstance(v, "Vertex")] verticesB = [v for v in verticesB if Topology.IsInstance(v, "Vertex")] if len(verticesA) < 1: print("Graph.Connect - Error: The input list of verticesA does not contain any valid vertices. Returning None.") return None if len(verticesB) < 1: print("Graph.Connect - Error: The input list of verticesB does not contain any valid vertices. Returning None.") return None if not len(verticesA) == len(verticesB): print("Graph.Connect - Error: The input lists verticesA and verticesB have different lengths. Returning None.") return None _ = graph.Connect(verticesA, verticesB, tolerance) return graph @staticmethod def ContainsEdge(graph, edge, tolerance=0.0001): """ Returns True if the input graph contains the input edge. Returns False otherwise. Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input edge. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- bool True if the input graph contains the input edge. False otherwise. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ContainsEdge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.ContainsEdge - Error: The input edge is not a valid edge. Returning None.") return None return graph.ContainsEdge(edge, tolerance) @staticmethod def ContainsVertex(graph, vertex, tolerance=0.0001): """ Returns True if the input graph contains the input Vertex. Returns False otherwise. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input Vertex. tolerance : float , optional Ther desired tolerance. The default is 0.0001. Returns ------- bool True if the input graph contains the input vertex. False otherwise. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ContainsVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.ContainsVertex - Error: The input vertex is not a valid vertex. Returning None.") return None return graph.ContainsVertex(vertex, tolerance) @staticmethod def DegreeSequence(graph): """ Returns the degree sequence of the input graph. See https://mathworld.wolfram.com/DegreeSequence.html. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- list The degree sequence of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.DegreeSequence - Error: The input graph is not a valid graph. Returning None.") return None sequence = [] _ = graph.DegreeSequence(sequence) return sequence @staticmethod def Density(graph): """ Returns the density of the input graph. See https://en.wikipedia.org/wiki/Dense_graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- float The density of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Density - Error: The input graph is not a valid graph. Returning None.") return None return graph.Density() @staticmethod def DepthMap(graph, vertices=None, tolerance=0.0001): """ Return the depth map of the input list of vertices within the input graph. The returned list contains the total of the topological distances of each vertex to every other vertex in the input graph. The order of the depth map list is the same as the order of the input list of vertices. If no vertices are specified, the depth map of all the vertices in the input graph is computed. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The depth map of the input list of vertices within the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.DepthMap - Error: The input graph is not a valid graph. Returning None.") return None graphVertices = Graph.Vertices(graph) if not isinstance(vertices, list): vertices = graphVertices else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.DepthMap - Error: The input list of vertices does not contain any valid vertices. Returning None.") return None depthMap = [] for va in vertices: depth = 0 for vb in graphVertices: if Topology.IsSame(va, vb): dist = 0 else: dist = Graph.TopologicalDistance(graph, va, vb, tolerance) depth = depth + dist depthMap.append(depth) return depthMap @staticmethod def Diameter(graph): """ Returns the diameter of the input graph. See https://mathworld.wolfram.com/GraphDiameter.html. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The diameter of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Diameter - Error: The input graph is not a valid graph. Returning None.") return None return graph.Diameter() @staticmethod def Dictionary(graph): """ Returns the dictionary of the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- topologic_core.Dictionary The dictionary of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Dictionary - Error: the input graph parameter is not a valid graph. Returning None.") return None return graph.GetDictionary() @staticmethod def Distance(graph, vertexA, vertexB, tolerance=0.0001): """ Returns the shortest-path distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory). Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- int The shortest-path distance between the input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Distance - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.Distance - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.Distance - Error: The input vertexB is not a valid vertex. Returning None.") return None return graph.TopologicalDistance(vertexA, vertexB, tolerance) @staticmethod def Edge(graph, vertexA, vertexB, tolerance=0.0001): """ Returns the edge in the input graph that connects in the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input Vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Edge The edge in the input graph that connects the input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Edge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.Edge - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.Edge - Error: The input vertexB is not a valid vertex. Returning None.") return None return graph.Edge(vertexA, vertexB, tolerance) @staticmethod def Edges(graph, vertices=None, tolerance=0.0001): """ Returns the edges found in the input graph. If the input list of vertices is specified, this method returns the edges connected to this list of vertices. Otherwise, it returns all graph edges. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional An optional list of vertices to restrict the returned list of edges only to those connected to this list. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of edges in the graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Edges - Error: The input graph is not a valid graph. Returning None.") return None if not vertices: edges = [] _ = graph.Edges(edges, tolerance) return edges else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.Edges - Error: The input list of vertices does not contain any valid vertices. Returning None.") return None edges = [] _ = graph.Edges(vertices, tolerance, edges) return list(dict.fromkeys(edges)) # remove duplicates @staticmethod def ExportToAdjacencyMatrixCSV(adjacencyMatrix, path): """ Exports the input graph into a set of CSV files compatible with DGL. Parameters ---------- path : str The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved. Returns ------- bool True if the graph has been successfully exported. False otherwise. """ # Convert the adjacency matrix (nested list) to a DataFrame adjacency_matrix_df = pd.DataFrame(adjacencyMatrix) # Export the DataFrame to a CSV file try: adjacency_matrix_df.to_csv(path, index=False, header=False) return True except: return False @staticmethod def ExportToBOT(graph, path, format="turtle", overwrite = False, bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel = "Site_0001", siteDictionary = None, buildingLabel = "Building_0001", buildingDictionary = None , storeyPrefix = "Storey", floorLevels =[], labelKey="label", typeKey="type", geometryKey="brep", spaceType = "space", wallType = "wall", slabType = "slab", doorType = "door", windowType = "window", contentType = "content", ): """ Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/. Parameters ---------- graph : topologic_core.Graph The input graph. format : str , optional The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads path : str The desired path to where the RDF/BOT file will be saved. overwrite : bool , optional If set to True, any existing file is overwritten. Otherwise, it is not. The default is False. bidirectional : bool , optional If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False. includeAttributes : bool , optional If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False. includeLabel : bool , optional If set to True, a label is attached to each node. Otherwise, it is not. The default is False. includeGeometry : bool , optional If set to True, the geometry associated with vertices in the graph are written out. Otherwise, it is not. The default is False. siteLabel : str , optional The desired site label. The default is "Site_0001". siteDictionary : dict , optional The dictionary of site attributes to include in the output. The default is None. buildingLabel : str , optional The desired building label. The default is "Building_0001". buildingDictionary : dict , optional The dictionary of building attributes to include in the output. The default is None. storeyPrefix : str , optional The desired prefixed to use for each building storey. The default is "Storey". floorLevels : list , optional The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the nodes' 'z' attribute. typeKey : str , optional The dictionary key to use to look up the type of the node. The default is "type". labelKey : str , optional The dictionary key to use to look up the label of the node. The default is "label". geometryKey : str , optional The dictionary key to use to look up the label of the node. The default is "brep". spaceType : str , optional The dictionary string value to use to look up nodes of type "space". The default is "space". wallType : str , optional The dictionary string value to use to look up nodes of type "wall". The default is "wall". slabType : str , optional The dictionary string value to use to look up nodes of type "slab". The default is "slab". doorType : str , optional The dictionary string value to use to look up nodes of type "door". The default is "door". windowType : str , optional The dictionary string value to use to look up nodes of type "window". The default is "window". contentType : str , optional The dictionary string value to use to look up nodes of type "content". The default is "contents". format : str , optional The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads Returns ------- str The rdf graph serialized string using the BOT ontology. """ from os.path import exists bot_graph = Graph.BOTGraph(graph, bidirectional=bidirectional, includeAttributes=includeAttributes, includeLabel=includeLabel, includeGeometry=includeGeometry, siteLabel=siteLabel, siteDictionary=siteDictionary, buildingLabel=buildingLabel, buildingDictionary=buildingDictionary, storeyPrefix=storeyPrefix, floorLevels=floorLevels, labelKey=labelKey, typeKey=typeKey, geometryKey=geometryKey, spaceType = spaceType, wallType = wallType, slabType = slabType, doorType = doorType, windowType = windowType, contentType = contentType ) if "turtle" in format.lower() or "ttl" in format.lower() or "turtle2" in format.lower(): ext = ".ttl" elif "xml" in format.lower() or "pretty=xml" in format.lower() or "rdf/xml" in format.lower(): ext = ".xml" elif "json" in format.lower(): ext = ".json" elif "ntriples" in format.lower() or "nt" in format.lower() or "nt11" in format.lower(): ext = ".nt" elif "n3" in format.lower() or "notation" in format.lower(): ext = ".n3" elif "trig" in format.lower(): ext = ".trig" elif "trix" in format.lower(): ext = ".trix" elif "nquads" in format.lower(): ext = ".nquads" else: format = "turtle" ext = ".ttl" n = len(ext) # Make sure the file extension is .brep ext = path[len(path)-n:len(path)] if ext.lower() != ext: path = path+ext if not overwrite and exists(path): print("Graph.ExportToBOT - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None status = False try: bot_graph.serialize(destination=path, format=format) status = True except: status = False return status @staticmethod def ExportToCSV(graph, path, graphLabel, graphFeatures="", graphIDHeader="graph_id", graphLabelHeader="label", graphFeaturesHeader="feat", edgeLabelKey="label", defaultEdgeLabel=0, edgeFeaturesKeys=[], edgeSRCHeader="src_id", edgeDSTHeader="dst_id", edgeLabelHeader="label", edgeFeaturesHeader="feat", edgeTrainMaskHeader="train_mask", edgeValidateMaskHeader="val_mask", edgeTestMaskHeader="test_mask", edgeMaskKey=None, edgeTrainRatio=0.8, edgeValidateRatio=0.1, edgeTestRatio=0.1, bidirectional=True, nodeLabelKey="label", defaultNodeLabel=0, nodeFeaturesKeys=[], nodeIDHeader="node_id", nodeLabelHeader="label", nodeFeaturesHeader="feat", nodeTrainMaskHeader="train_mask", nodeValidateMaskHeader="val_mask", nodeTestMaskHeader="test_mask", nodeMaskKey=None, nodeTrainRatio=0.8, nodeValidateRatio=0.1, nodeTestRatio=0.1, mantissa=6, overwrite=False): """ Exports the input graph into a set of CSV files compatible with DGL. Parameters ---------- graph : topologic_core.Graph The input graph path : str The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved. graphLabel : float or int The input graph label. This can be an int (categorical) or a float (continous) graphFeatures : str , optional The input graph features. This is a single string of numeric features separated by commas. Example: "3.456, 2.011, 56.4". The defauly is "". graphIDHeader : str , optional The desired graph ID column header. The default is "graph_id". graphLabelHeader : str , optional The desired graph label column header. The default is "label". graphFeaturesHeader : str , optional The desired graph features column header. The default is "feat". edgeLabelKey : str , optional The edge label dictionary key saved in each graph edge. The default is "label". defaultEdgeLabel : int , optional The default edge label to use if no edge label is found. The default is 0. edgeSRCHeader : str , optional The desired edge source column header. The default is "src_id". edgeDSTHeader : str , optional The desired edge destination column header. The default is "dst_id". edgeFeaturesHeader : str , optional The desired edge features column header. The default is "feat". edgeFeaturesKeys : list , optional The list of feature dictionary keys saved in the dicitonaries of edges. The default is []. edgeTrainMaskHeader : str , optional The desired edge train mask column header. The default is "train_mask". edgeValidateMaskHeader : str , optional The desired edge validate mask column header. The default is "val_mask". edgeTestMaskHeader : str , optional The desired edge test mask column header. The default is "test_mask". edgeMaskKey : str , optional The dictionary key where the edge train, validate, test category is to be found. The value should be 0 for train 1 for validate, and 2 for test. If no key is found, the ratio of train/validate/test will be used. The default is "mask". edgeTrainRatio : float , optional The desired ratio of the edge data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training. This value is ignored if an edgeMaskKey is foud. edgeValidateRatio : float , optional The desired ratio of the edge data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation. This value is ignored if an edgeMaskKey is foud. edgeTestRatio : float , optional The desired ratio of the edge data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing. This value is ignored if an edgeMaskKey is foud. bidirectional : bool , optional If set to True, a reversed edge will also be saved for each edge in the graph. Otherwise, it will not. The default is True. nodeFeaturesKeys : list , optional The list of features keys saved in the dicitonaries of nodes. The default is []. nodeLabelKey : str , optional The node label dictionary key saved in each graph vertex. The default is "label". defaultNodeLabel : int , optional The default node label to use if no node label is found. The default is 0. nodeIDHeader : str , optional The desired node ID column header. The default is "node_id". nodeLabelHeader : str , optional The desired node label column header. The default is "label". nodeFeaturesHeader : str , optional The desired node features column header. The default is "feat". nodeTrainMaskHeader : str , optional The desired node train mask column header. The default is "train_mask". nodeValidateMaskHeader : str , optional The desired node validate mask column header. The default is "val_mask". nodeTestMaskHeader : str , optional The desired node test mask column header. The default is "test_mask". nodeTrainRatio : float , optional The desired ratio of the node data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training. This value is ignored if an nodeMaskKey is foud. nodeValidateRatio : float , optional The desired ratio of the node data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation. This value is ignored if an nodeMaskKey is foud. nodeTestRatio : float , optional The desired ratio of the node data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing. This value is ignored if an nodeMaskKey is foud. mantissa : int , optional The desired length of the mantissa. The default is 6. overwrite : bool , optional If set to True, any existing files are overwritten. Otherwise, the input list of graphs is appended to the end of each file. The default is False. Returns ------- bool True if the graph has been successfully exported. False otherwise. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import os import math import random from os.path import exists if not Topology.IsInstance(graph, "Graph"): print("Graph.ExportToCSV - Error: The input graph parameter is not a valid topologic graph. Returning None.") return None if not exists(path): try: os.mkdir(path) except: print("Graph.ExportToCSV - Error: Could not create a folder at the specified path parameter. Returning None.") return None if overwrite == False: if not exists(os.path.join(path, "graphs.csv")): print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.") return None if not exists(os.path.join(path, "edges.csv")): print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.") return None if not exists(os.path.join(path, "nodes.csv")): print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.") return None if abs(nodeTrainRatio + nodeValidateRatio + nodeTestRatio - 1) > 0.001: print("Graph.ExportToCSV - Error: The node train, validate, test ratios do not add up to 1. Returning None") return None if abs(edgeTrainRatio + edgeValidateRatio + edgeTestRatio - 1) > 0.001: print("Graph.ExportToCSV - Error: The edge train, validate, test ratios do not add up to 1. Returning None") return None # Step 1: Export Graphs if overwrite == False: graphs = pd.read_csv(os.path.join(path,"graphs.csv")) max_id = max(list(graphs[graphIDHeader])) graph_id = max_id + 1 else: graph_id = 0 data = [[graph_id], [graphLabel], [graphFeatures]] columns = [graphIDHeader, graphLabelHeader, graphFeaturesHeader] # Write Graph Data to CSV file data = Helper.Iterate(data) data = Helper.Transpose(data) df = pd.DataFrame(data, columns=columns) if overwrite == False: df.to_csv(os.path.join(path, "graphs.csv"), mode='a', index = False, header=False) else: df.to_csv(os.path.join(path, "graphs.csv"), mode='w+', index = False, header=True) # Step 2: Export Nodes vertices = Graph.Vertices(graph) if len(vertices) < 3: print("Graph.ExportToCSV - Error: The graph is too small to be used. Returning None") return None # Shuffle the vertices vertices = random.sample(vertices, len(vertices)) node_train_max = math.floor(float(len(vertices))*nodeTrainRatio) if node_train_max == 0: node_train_max = 1 node_validate_max = math.floor(float(len(vertices))*nodeValidateRatio) if node_validate_max == 0: node_validate_max = 1 node_test_max = len(vertices) - node_train_max - node_validate_max if node_test_max == 0: node_test_max = 1 node_data = [] node_columns = [graphIDHeader, nodeIDHeader, nodeLabelHeader, nodeTrainMaskHeader, nodeValidateMaskHeader, nodeTestMaskHeader, nodeFeaturesHeader, "X", "Y", "Z"] train = 0 test = 0 validate = 0 for i, v in enumerate(vertices): # Get the node label nd = Topology.Dictionary(v) vLabel = Dictionary.ValueAtKey(nd, nodeLabelKey) if vLabel == None: vLabel = defaultNodeLabel # Get the train/validate/test mask value flag = False if not nodeMaskKey == None: if not nd == None: keys = Dictionary.Keys(nd) else: keys = [] flag = True if nodeMaskKey in keys: value = Dictionary.ValueAtKey(nd, nodeMaskKey) if not value in [0, 1, 2]: flag = True elif value == 0: train_mask = True validate_mask = False test_mask = False train = train + 1 elif value == 1: train_mask = False validate_mask = True test_mask = False validate = validate + 1 else: train_mask = False validate_mask = False test_mask = True test = test + 1 else: flag = True else: flag = True if flag: if train < node_train_max: train_mask = True validate_mask = False test_mask = False train = train + 1 elif validate < node_validate_max: train_mask = False validate_mask = True test_mask = False validate = validate + 1 elif test < node_test_max: train_mask = False validate_mask = False test_mask = True test = test + 1 else: train_mask = True validate_mask = False test_mask = False train = train + 1 # Get the features of the vertex node_features = "" node_features_keys = Helper.Flatten(nodeFeaturesKeys) for node_feature_key in node_features_keys: if len(node_features) > 0: node_features = node_features + ","+ str(round(float(Dictionary.ValueAtKey(nd, node_feature_key)),mantissa)) else: node_features = str(round(float(Dictionary.ValueAtKey(nd, node_feature_key)),mantissa)) single_node_data = [graph_id, i, vLabel, train_mask, validate_mask, test_mask, node_features, round(float(Vertex.X(v)),mantissa), round(float(Vertex.Y(v)),mantissa), round(float(Vertex.Z(v)),mantissa)] node_data.append(single_node_data) # Write Node Data to CSV file df = pd.DataFrame(node_data, columns= node_columns) if graph_id == 0: df.to_csv(os.path.join(path, "nodes.csv"), mode='w+', index = False, header=True) else: df.to_csv(os.path.join(path, "nodes.csv"), mode='a', index = False, header=False) # Step 3: Export Edges edge_data = [] edge_columns = [graphIDHeader, edgeSRCHeader, edgeDSTHeader, edgeLabelHeader, edgeTrainMaskHeader, edgeValidateMaskHeader, edgeTestMaskHeader, edgeFeaturesHeader] train = 0 test = 0 validate = 0 edges = Graph.Edges(graph) edge_train_max = math.floor(float(len(edges))*edgeTrainRatio) edge_validate_max = math.floor(float(len(edges))*edgeValidateRatio) edge_test_max = len(edges) - edge_train_max - edge_validate_max for edge in edges: # Get the edge label ed = Topology.Dictionary(edge) edge_label = Dictionary.ValueAtKey(ed, edgeLabelKey) if edge_label == None: edge_label = defaultEdgeLabel # Get the train/validate/test mask value flag = False if not edgeMaskKey == None: if not ed == None: keys = Dictionary.Keys(ed) else: keys = [] flag = True if edgeMaskKey in keys: value = Dictionary.ValueAtKey(ed, edgeMaskKey) if not value in [0, 1, 2]: flag = True elif value == 0: train_mask = True validate_mask = False test_mask = False train = train + 1 elif value == 1: train_mask = False validate_mask = True test_mask = False validate = validate + 1 else: train_mask = False validate_mask = False test_mask = True test = test + 1 else: flag = True else: flag = True if flag: if train < edge_train_max: train_mask = True validate_mask = False test_mask = False train = train + 1 elif validate < edge_validate_max: train_mask = False validate_mask = True test_mask = False validate = validate + 1 elif test < edge_test_max: train_mask = False validate_mask = False test_mask = True test = test + 1 else: train_mask = True validate_mask = False test_mask = False train = train + 1 # Get the edge features edge_features = "" edge_features_keys = Helper.Flatten(edgeFeaturesKeys) for edge_feature_key in edge_features_keys: if len(edge_features) > 0: edge_features = edge_features + ","+ str(round(float(Dictionary.ValueAtKey(ed, edge_feature_key)),mantissa)) else: edge_features = str(round(float(Dictionary.ValueAtKey(ed, edge_feature_key)), mantissa)) # Get the Source and Destination vertex indices src = Vertex.Index(Edge.StartVertex(edge), vertices) dst = Vertex.Index(Edge.EndVertex(edge), vertices) single_edge_data = [graph_id, src, dst, edge_label, train_mask, validate_mask, test_mask, edge_features] edge_data.append(single_edge_data) if bidirectional == True: single_edge_data = [graph_id, src, dst, edge_label, train_mask, validate_mask, test_mask, edge_features] edge_data.append(single_edge_data) df = pd.DataFrame(edge_data, columns=edge_columns) if graph_id == 0: df.to_csv(os.path.join(path, "edges.csv"), mode='w+', index = False, header=True) else: df.to_csv(os.path.join(path, "edges.csv"), mode='a', index = False, header=False) # Write out the meta.yaml file yaml_file = open(os.path.join(path,"meta.yaml"), "w") if graph_id > 0: yaml_file.write('dataset_name: topologic_dataset\nedge_data:\n- file_name: edges.csv\nnode_data:\n- file_name: nodes.csv\ngraph_data:\n file_name: graphs.csv') else: yaml_file.write('dataset_name: topologic_dataset\nedge_data:\n- file_name: edges.csv\nnode_data:\n- file_name: nodes.csv') yaml_file.close() return True @staticmethod def ExportToGEXF(graph, path, graphWidth=20, graphLength=20, graphHeight=20, defaultVertexColor="black", defaultVertexSize=3, vertexLabelKey=None, vertexColorKey=None, vertexSizeKey=None, defaultEdgeColor="black", defaultEdgeWeight=1, defaultEdgeType="undirected", edgeLabelKey=None, edgeColorKey=None, edgeWeightKey=None, overwrite=False): """ Exports the input graph to a Graph Exchange XML (GEXF) file format. See https://gexf.net/ Parameters ---------- graph : topologic_core.Graph The input graph path : str The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved. graphWidth : float or int , optional The desired graph width. The default is 20. graphLength : float or int , optional The desired graph length. The default is 20. graphHeight : float or int , optional The desired graph height. The default is 20. defaultVertexColor : str , optional The desired default vertex color. The default is "black". defaultVertexSize : float or int , optional The desired default vertex size. The default is 3. defaultEdgeColor : str , optional The desired default edge color. The default is "black". defaultEdgeWeight : float or int , optional The desired default edge weight. The edge weight determines the width of the displayed edge. The default is 3. defaultEdgeType : str , optional The desired default edge type. This can be one of "directed" or "undirected". The default is "undirected". vertexLabelKey : str , optional If specified, the vertex dictionary is searched for this key to determine the vertex label. If not specified the vertex label being is set to "Node X" where is X is a unique number. The default is None. vertexColorKey : str , optional If specified, the vertex dictionary is searched for this key to determine the vertex color. If not specified the vertex color is set to the value defined by defaultVertexColor parameter. The default is None. vertexSizeKey : str , optional If specified, the vertex dictionary is searched for this key to determine the vertex size. If not specified the vertex size is set to the value defined by defaultVertexSize parameter. The default is None. edgeLabelKey : str , optional If specified, the edge dictionary is searched for this key to determine the edge label. If not specified the edge label being is set to "Edge X" where is X is a unique number. The default is None. edgeColorKey : str , optional If specified, the edge dictionary is searched for this key to determine the edge color. If not specified the edge color is set to the value defined by defaultEdgeColor parameter. The default is None. edgeWeightKey : str , optional If specified, the edge dictionary is searched for this key to determine the edge weight. If not specified the edge weight is set to the value defined by defaultEdgeWeight parameter. The default is None. overwrite : bool , optional If set to True, any existing file is overwritten. Otherwise, it is not. The default is False. Returns ------- bool True if the graph has been successfully exported. False otherwise. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Color import Color import numbers from datetime import datetime import os from os.path import exists def create_gexf_file(nodes, edges, default_edge_type, node_attributes, edge_attributes, path): with open(path, 'w') as file: # Write the GEXF header formatted_date = datetime.now().strftime("%Y-%m-%d") if not isinstance(default_edge_type, str): default_edge_type = "undirected" if default_edge_type.lower() == "directed": defaultedge_type = "directed" else: default_edge_type = "undirected" file.write('<?xml version="1.0" encoding="UTF-8"?>\n') file.write('<gexf version="1.3" xmlns="http://www.gephi.org/gexf" xmlns:viz="http://www.gephi.org/gexf/viz">\n') file.write(f'<meta lastmodifieddate="{formatted_date}">\n') file.write('<creator>Topologic GEXF Generator</creator>\n') file.write('<title>"Topologic Graph"</title>\n') file.write('<description>"This is a Topologic Graph"</description>\n') file.write('</meta>\n') file.write(f'<graph type="static" defaultedgetype="{defaultEdgeType}">\n') # Write attribute definitions file.write('<attributes class="node" mode="static">\n') for attr_name, attr_type in node_attributes.items(): file.write(f'<attribute id="{attr_name}" title="{attr_name}" type="{attr_type}"/>\n') file.write('</attributes>\n') # Write attribute definitions file.write('<attributes class="edge" mode="static">\n') for attr_name, attr_type in edge_attributes.items(): file.write(f'<attribute id="{attr_name}" title="{attr_name}" type="{attr_type}"/>\n') file.write('</attributes>\n') # Write nodes with attributes file.write('<nodes>\n') for node_id, node_attrs in nodes.items(): file.write(f'<node id="{node_id}" label="{node_attrs["label"]}">\n') if "r" in node_attrs and "g" in node_attrs and "b" in node_attrs: r = node_attrs['r'] g = node_attrs['g'] b = node_attrs['b'] file.write(f'<viz:color r="{r}" g="{g}" b="{b}"/>\n') if "size" in node_attrs: file.write(f'<viz:size value="{node_attrs["size"]}"/>\n') if "x" in node_attrs and "y" in node_attrs and "z" in node_attrs: file.write(f'<viz:position x="{node_attrs["x"]}" y="{node_attrs["y"]}" z="{node_attrs["z"]}"/>\n') file.write('<attvalues>\n') keys = node_attrs.keys() for key in keys: file.write(f'<attvalue id="{key}" value="{node_attrs[key]}"/>\n') file.write('</attvalues>\n') file.write('</node>\n') file.write('</nodes>\n') # Write edges with attributes file.write('<edges>\n') for edge_id, edge_attrs in edges.items(): source, target = edge_id file.write(f'<edge id="{edge_id}" source="{source}" target="{target}" label="{edge_attrs["label"]}">\n') if "color" in edge_attrs: r, g, b = Color.ByCSSNamedColor(edge_attrs["color"]) file.write(f'<viz:color r="{r}" g="{g}" b="{b}"/>\n') file.write('<attvalues>\n') keys = edge_attrs.keys() for key in keys: file.write(f'<attvalue id="{key}" value="{edge_attrs[key]}"/>\n') file.write('</attvalues>\n') file.write('</edge>\n') file.write('</edges>\n') # Write the GEXF footer file.write('</graph>\n') file.write('</gexf>\n') def valueType(value): if isinstance(value, str): return 'string' elif isinstance(value, float): return 'double' elif isinstance(value, int): return 'integer' else: return 'string' if not Topology.IsInstance(graph, "Graph"): print("Graph.ExportToGEXF - Error: the input graph parameter is not a valid graph. Returning None.") return None if not isinstance(path, str): print("Graph.ExportToGEXF - Error: the input path parameter is not a valid string. Returning None.") return None # Make sure the file extension is .brep ext = path[len(path)-5:len(path)] if ext.lower() != ".gexf": path = path+".gexf" if not overwrite and exists(path): print("Graph.ExportToGEXF - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None g_vertices = Graph.Vertices(graph) g_edges = Graph.Edges(graph) node_attributes = {'id': 'integer', 'label': 'string', 'x': 'double', 'y': 'double', 'z': 'double', 'r': 'integer', 'g': 'integer', 'b': 'integer', 'color': 'string', 'size': 'double'} nodes = {} # Resize the graph xList = [Vertex.X(v) for v in g_vertices] yList = [Vertex.Y(v) for v in g_vertices] zList = [Vertex.Z(v) for v in g_vertices] xMin = min(xList) xMax = max(xList) yMin = min(yList) yMax = max(yList) zMin = min(zList) zMax = max(zList) width = max(abs(xMax - xMin), 0.01) length = max(abs(yMax - yMin), 0.01) height = max(abs(zMax - zMin), 0.01) x_sf = graphWidth/width y_sf = graphLength/length z_sf = graphHeight/height x_avg = sum(xList)/float(len(xList)) y_avg = sum(yList)/float(len(yList)) z_avg = sum(zList)/float(len(zList)) for i, v in enumerate(g_vertices): node_dict = {} d = Topology.Dictionary(v) keys = Dictionary.Keys(d) values = Dictionary.Values(d) x = (Vertex.X(v) - x_avg)*x_sf + x_avg y = (Vertex.Y(v) - y_avg)*y_sf + y_avg z = (Vertex.Z(v) - z_avg)*z_sf + z_avg node_dict['x'] = x node_dict['y'] = y node_dict['z'] = z node_dict['id'] = i for m, key in enumerate(keys): if key == "psets": #We cannot handle IFC psets at this point. continue if key == "id": key = "TOPOLOGIC_ID" if not key in node_attributes.keys(): node_attributes[key] = valueType(values[m]) if isinstance(values[m], str): values[m] = values[m].replace('&','&') values[m] = values[m].replace('<','<') values[m] = values[m].replace('>','>') values[m] = values[m].replace('"','"') values[m] = values[m].replace('\'',''') node_dict[key] = values[m] dict_color = None if not defaultVertexColor in Color.CSSNamedColors(): defaultVertexColor = "black" vertex_color = defaultVertexColor if isinstance(vertexColorKey, str): dict_color = Dictionary.ValueAtKey(d, vertexColorKey) if not dict_color == None: vertex_color = dict_color if not vertex_color in Color.CSSNamedColors(): vertex_color = defaultVertexColor node_dict['color'] = vertex_color r, g, b = Color.ByCSSNamedColor(vertex_color) node_dict['r'] = r node_dict['g'] = g node_dict['b'] = b dict_size = None if isinstance(vertexSizeKey, str): dict_size = Dictionary.ValueAtKey(d, vertexSizeKey) vertex_size = defaultVertexSize if not dict_size == None: if isinstance(dict_size, numbers.Real): vertex_size = dict_size if not isinstance(vertex_size, numbers.Real): vertex_size = defaultVertexSize node_dict['size'] = vertex_size vertex_label = "Node "+str(i) if isinstance(vertexLabelKey, str): vertex_label = Dictionary.ValueAtKey(d, vertexLabelKey) if not isinstance(vertex_label, str): vertex_label = "Node "+str(i) if isinstance(vertex_label, str): vertex_label = vertex_label.replace('&','&') vertex_label = vertex_label.replace('<','<') vertex_label = vertex_label.replace('>','>') vertex_label = vertex_label.replace('"','"') vertex_label = vertex_label.replace('\'',''') node_dict['label'] = vertex_label nodes[i] = node_dict edge_attributes = {'id': 'integer', 'label': 'string', 'source': 'integer', 'target': 'integer', 'r': 'integer', 'g': 'integer', 'b': 'integer', 'color': 'string', 'weight': 'double'} edges = {} for i, edge in enumerate(g_edges): edge_dict = {} d = Topology.Dictionary(edge) keys = Dictionary.Keys(d) values = Dictionary.Values(d) edge_dict['id'] = i for m, key in enumerate(keys): if key == "id": key = "TOPOLOGIC_ID" if not key in edge_attributes.keys(): edge_attributes[key] = valueType(values[m]) edge_dict[key] = values[m] dict_color = None if not defaultEdgeColor in Color.CSSNamedColors(): defaultEdgeColor = "black" edge_color = defaultEdgeColor if isinstance(edgeColorKey, str): dict_color = Dictionary.ValueAtKey(d, edgeColorKey) if not dict_color == None: edge_color = dict_color if not vertex_color in Color.CSSNamedColors(): edge_color = defaultVertexColor edge_dict['color'] = edge_color r, g, b = Color.ByCSSNamedColor(edge_color) edge_dict['r'] = r edge_dict['g'] = g edge_dict['b'] = b dict_weight = None if not isinstance(defaultEdgeWeight, numbers.Real): defaultEdgeWeight = 1 edge_weight = defaultEdgeWeight if isinstance(edgeWeightKey, str): dict_weight = Dictionary.ValueAtKey(d, edgeWeightKey) if not dict_weight == None: if isinstance(dict_weight, numbers.Real): edge_weight = dict_weight if not isinstance(edge_weight, numbers.Real): edge_weight = defaultEdgeWeight edge_dict['weight'] = edge_weight sv = g_vertices[Vertex.Index(Edge.StartVertex(edge), g_vertices)] ev = g_vertices[Vertex.Index(Edge.EndVertex(edge), g_vertices)] svid = Vertex.Index(sv, g_vertices) edge_dict['source'] = svid evid = Vertex.Index(ev, g_vertices) edge_dict['target'] = evid edge_label = "Edge "+str(svid)+"-"+str(evid) if isinstance(edgeLabelKey, str): edge_label = Dictionary.ValueAtKey(d, edgeLabelKey) if not isinstance(edge_label, str): edge_label = "Edge "+str(svid)+"-"+str(evid) edge_dict['label'] = edge_label edges[(str(svid), str(evid))] = edge_dict create_gexf_file(nodes, edges, defaultEdgeType, node_attributes, edge_attributes, path) return True @staticmethod def ExportToJSON(graph, path, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", xKey="x", yKey="y", zKey="z", indent=4, sortKeys=False, mantissa=6, overwrite=False): """ Exports the input graph to a JSON file. Parameters ---------- graph : topologic_core.Graph The input graph. path : str The path to the JSON file. verticesKey : str , optional The desired key name to call vertices. The default is "vertices". edgesKey : str , optional The desired key name to call edges. The default is "edges". vertexLabelKey : str , optional If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique. edgeLabelKey : str , optional If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique. xKey : str , optional The desired key name to use for x-coordinates. The default is "x". yKey : str , optional The desired key name to use for y-coordinates. The default is "y". zKey : str , optional The desired key name to use for z-coordinates. The default is "z". indent : int , optional The desired amount of indent spaces to use. The default is 4. sortKeys : bool , optional If set to True, the keys will be sorted. Otherwise, they won't be. The default is False. mantissa : int , optional The desired length of the mantissa. The default is 6. overwrite : bool , optional If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False. Returns ------- bool The status of exporting the JSON file. If True, the operation was successful. Otherwise, it was unsuccesful. """ import json from os.path import exists # Make sure the file extension is .json ext = path[len(path)-5:len(path)] if ext.lower() != ".json": path = path+".json" if not overwrite and exists(path): print("Graph.ExportToJSON - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None f = None try: if overwrite == True: f = open(path, "w") else: f = open(path, "x") # Try to create a new File except: raise Exception("Graph.ExportToJSON - Error: Could not create a new file at the following location: "+path) if (f): jsondata = Graph.JSONData(graph, verticesKey=verticesKey, edgesKey=edgesKey, vertexLabelKey=vertexLabelKey, edgeLabelKey=edgeLabelKey, xKey=xKey, yKey=yKey, zKey=zKey, mantissa=mantissa) if jsondata != None: json.dump(jsondata, f, indent=indent, sort_keys=sortKeys) f.close() return True else: f.close() return False return False @staticmethod def Flatten(graph, layout="spring", k=0.8, seed=None, iterations=50, rootVertex=None, tolerance=0.0001): """ Flattens the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. layout : str , optional The desired mode for flattening. If set to 'spring', the algorithm uses a simplified version of the Fruchterman-Reingold force-directed algorithm to flatten and distribute the vertices. If set to 'radial', the nodes will be distributed along a circle. If set to 'tree', the nodes will be distributed using the Reingold-Tillford layout. The default is 'spring'. k : float, optional The desired spring constant to use for the attractive and repulsive forces. The default is 0.8. seed : int , optional The desired random seed to use. The default is None. iterations : int , optional The desired maximum number of iterations to solve the forces in the 'spring' mode. The default is 50. rootVertex : topologic_core.Vertex , optional The desired vertex to use as the root of the tree and radial layouts. Returns ------- topologic_core.Graph The flattened graph. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology import numpy as np def buchheim(tree): dt = firstwalk(_DrawTree(tree)) min = second_walk(dt) if min < 0: third_walk(dt, -min) return dt def third_walk(tree, n): tree.x += n for c in tree.children: third_walk(c, n) def firstwalk(v, distance=1.0): if len(v.children) == 0: if v.lmost_sibling: v.x = v.lbrother().x + distance else: v.x = 0.0 else: default_ancestor = v.children[0] for w in v.children: firstwalk(w) default_ancestor = apportion(w, default_ancestor, distance) execute_shifts(v) midpoint = (v.children[0].x + v.children[-1].x) / 2 w = v.lbrother() if w: v.x = w.x + distance v.mod = v.x - midpoint else: v.x = midpoint return v def apportion(v, default_ancestor, distance): w = v.lbrother() if w is not None: vir = vor = v vil = w vol = v.lmost_sibling sir = sor = v.mod sil = vil.mod sol = vol.mod while vil.right() and vir.left(): vil = vil.right() vir = vir.left() vol = vol.left() vor = vor.right() vor.ancestor = v shift = (vil.x + sil) - (vir.x + sir) + distance if shift > 0: move_subtree(ancestor(vil, v, default_ancestor), v, shift) sir = sir + shift sor = sor + shift sil += vil.mod sir += vir.mod sol += vol.mod sor += vor.mod if vil.right() and not vor.right(): vor.thread = vil.right() vor.mod += sil - sor else: if vir.left() and not vol.left(): vol.thread = vir.left() vol.mod += sir - sol default_ancestor = v return default_ancestor def move_subtree(wl, wr, shift): subtrees = wr.number - wl.number wr.change -= shift / subtrees wr.shift += shift wl.change += shift / subtrees wr.x += shift wr.mod += shift def execute_shifts(v): shift = change = 0 for w in v.children[::-1]: w.x += shift w.mod += shift change += w.change shift += w.shift + change def ancestor(vil, v, default_ancestor): if vil.ancestor in v.parent.children: return vil.ancestor else: return default_ancestor def second_walk(v, m=0, depth=0, min=None): v.x += m v.y = depth if min is None or v.x < min: min = v.x for w in v.children: min = second_walk(w, m + v.mod, depth + 1, min) return min def edge_list_to_adjacency_matrix(edge_list): """Converts an edge list to an adjacency matrix. Args: edge_list: A list of tuples, where each tuple is an edge. Returns: A numpy array representing the adjacency matrix. """ # Get the number of nodes from the edge list. num_nodes = max([max(edge) for edge in edge_list]) + 1 # Create an adjacency matrix. adjacency_matrix = np.zeros((num_nodes, num_nodes)) # Fill in the adjacency matrix. for edge in edge_list: adjacency_matrix[edge[0], edge[1]] = 1 adjacency_matrix[edge[1], edge[0]] = 1 return adjacency_matrix def tree_from_edge_list(edge_list, root_index=0): adj_matrix = edge_list_to_adjacency_matrix(edge_list) num_nodes = adj_matrix.shape[0] root = _Tree(str(root_index)) is_visited = np.zeros(num_nodes) is_visited[root_index] = 1 old_roots = [root] new_roots = [] while(np.sum(is_visited) < num_nodes): new_roots = [] for temp_root in old_roots: children = [] for i in range(num_nodes): if adj_matrix[int(temp_root.node), i] == 1 and is_visited[i] == 0: is_visited[i] = 1 child = _Tree(str(i)) temp_root.children.append(child) children.append(child) new_roots.extend(children) old_roots = new_roots return root, num_nodes def spring_layout(edge_list, iterations=500, k=None, seed=None): # Compute the layout of a graph using the Fruchterman-Reingold algorithm # with a force-directed layout approach. adj_matrix = edge_list_to_adjacency_matrix(edge_list) # Set the random seed if seed is not None: np.random.seed(seed) # Set the optimal distance between nodes if k is None or k <= 0: k = np.sqrt(1.0 / adj_matrix.shape[0]) # Initialize the positions of the nodes randomly pos = np.random.rand(adj_matrix.shape[0], 2) # Compute the initial temperature t = 0.1 * np.max(pos) # Compute the cooling factor cooling_factor = t / iterations # Iterate over the specified number of iterations for i in range(iterations): # Compute the distance between each pair of nodes delta = pos[:, np.newaxis, :] - pos[np.newaxis, :, :] distance = np.linalg.norm(delta, axis=-1) # Avoid division by zero distance = np.where(distance == 0, 0.1, distance) # Compute the repulsive force between each pair of nodes repulsive_force = k ** 2 / distance ** 2 # Compute the attractive force between each pair of adjacent nodes attractive_force = adj_matrix * distance / k # Compute the total force acting on each node force = np.sum((repulsive_force - attractive_force)[:, :, np.newaxis] * delta, axis=1) # Compute the displacement of each node displacement = t * force / np.linalg.norm(force, axis=1)[:, np.newaxis] # Update the positions of the nodes pos += displacement # Cool the temperature t -= cooling_factor return pos def tree_layout(edge_list, root_index=0): root, num_nodes = tree_from_edge_list(edge_list, root_index) dt = buchheim(root) pos = np.zeros((num_nodes, 2)) pos[int(dt.tree.node), 0] = dt.x pos[int(dt.tree.node), 1] = dt.y old_roots = [dt] new_roots = [] while(len(old_roots) > 0): new_roots = [] for temp_root in old_roots: children = temp_root.children for child in children: pos[int(child.tree.node), 0] = child.x pos[int(child.tree.node), 1] = child.y new_roots.extend(children) old_roots = new_roots pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1] return pos def radial_layout(edge_list, root_index=0): root, num_nodes = tree_from_edge_list(edge_list, root_index) dt = buchheim(root) pos = np.zeros((num_nodes, 2)) pos[int(dt.tree.node), 0] = dt.x pos[int(dt.tree.node), 1] = dt.y old_roots = [dt] new_roots = [] while(len(old_roots) > 0): new_roots = [] for temp_root in old_roots: children = temp_root.children for child in children: pos[int(child.tree.node), 0] = child.x pos[int(child.tree.node), 1] = child.y new_roots.extend(children) old_roots = new_roots # pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1] pos[:, 0] = pos[:, 0] - np.min(pos[:, 0]) pos[:, 1] = pos[:, 1] - np.min(pos[:, 1]) pos[:, 0] = pos[:, 0] / np.max(pos[:, 0]) pos[:, 0] = pos[:, 0] - pos[:, 0][root_index] range_ = np.max(pos[:, 0]) - np.min(pos[:, 0]) pos[:, 0] = pos[:, 0] / range_ pos[:, 0] = pos[:, 0] * np.pi * 1.98 pos[:, 1] = pos[:, 1] / np.max(pos[:, 1]) new_pos = np.zeros((num_nodes, 2)) new_pos[:, 0] = pos[:, 1] * np.cos(pos[:, 0]) new_pos[:, 1] = pos[:, 1] * np.sin(pos[:, 0]) return new_pos def graph_layout(edge_list, layout='tree', root_index=0, k=None, seed=None, iterations=500): if layout == 'tree': return tree_layout(edge_list, root_index=root_index) elif layout == 'spring': return spring_layout(edge_list, k=k, seed=seed, iterations=iterations) elif layout == 'radial': return radial_layout(edge_list, root_index=root_index) else: raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']") def vertex_max_degree(graph, vertices): degrees = [Graph.VertexDegree(graph, vertex) for vertex in vertices] i = degrees.index(max(degrees)) return vertices[i], i if not Topology.IsInstance(graph, "Graph"): print("Graph.Flatten - Error: The input graph is not a valid topologic graph. Returning None.") return None d = Graph.MeshData(graph) vertices = d['vertices'] edges = d['edges'] v_dicts = d['vertexDictionaries'] e_dicts = d['edgeDictionaries'] vertices = Graph.Vertices(graph) if rootVertex == None: rootVertex, root_index = vertex_max_degree(graph, vertices) else: root_index = Vertex.Index(rootVertex, vertices) if 'rad' in layout.lower(): positions = radial_layout(edges, root_index=root_index) elif 'spring' in layout.lower(): positions = spring_layout(edges, k=k, seed=seed, iterations=iterations) elif 'tree' in layout.lower(): positions = tree_layout(edges, root_index=root_index) else: raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']") positions = positions.tolist() positions = [[p[0], p[1], 0] for p in positions] flat_graph = Graph.ByMeshData(positions, edges, v_dicts, e_dicts, tolerance=tolerance) return flat_graph @staticmethod def GlobalClusteringCoefficient(graph): """ Returns the global clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The computed global clustering coefficient. """ from topologicpy.Topology import Topology def global_clustering_coefficient(adjacency_matrix): total_triangles = 0 total_possible_triangles = 0 num_nodes = len(adjacency_matrix) for i in range(num_nodes): neighbors = [j for j, value in enumerate(adjacency_matrix[i]) if value == 1] num_neighbors = len(neighbors) num_triangles = 0 if num_neighbors >= 2: # Count the number of connections between the neighbors for i in range(num_neighbors): for j in range(i + 1, num_neighbors): if adjacency_matrix[neighbors[i]][neighbors[j]] == 1: num_triangles += 1 # Update total triangles and possible triangles total_triangles += num_triangles total_possible_triangles += num_neighbors * (num_neighbors - 1) // 2 # Calculate the global clustering coefficient global_clustering_coeff = 3.0 * total_triangles / total_possible_triangles if total_possible_triangles > 0 else 0.0 return global_clustering_coeff if not Topology.IsInstance(graph, "Graph"): print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.") return None adjacency_matrix = Graph.AdjacencyMatrix(graph) return global_clustering_coefficient(adjacency_matrix) @staticmethod def Guid(graph): """ Returns the guid of the input graph Parameters ---------- graph : topologic_core.Graph The input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Guid - Error: the input graph parameter is not a valid graph. Returning None.") return None return graph.GetGUID() @staticmethod def IncomingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the incoming edges connected to a vertex. An edge is considered incoming if its end vertex is coincident with the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of incoming edges """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IncomingEdges - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.IncomingEdges - Error: The input vertex parameter is not a valid vertex. Returning None.") return None edges = Graph.Edges(graph, [vertex]) if directed == False: return edges incoming_edges = [] for edge in edges: ev = Edge.EndVertex(edge) if Vertex.Distance(vertex, ev) < tolerance: incoming_edges.append(edge) return incoming_edges @staticmethod def IncomingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the incoming vertices connected to a vertex. A vertex is considered incoming if it is an adjacent vertex to the input vertex and the the edge connecting it to the input vertex is an incoming edge. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of incoming vertices """ from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IncomingVertices - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.IncomingVertices - Error: The input vertex parameter is not a valid vertex. Returning None.") return None if directed == False: return Graph.AdjacentVertices(graph, vertex) incoming_edges = Graph.IncomingEdges(graph, vertex, directed=directed, tolerance=tolerance) incoming_vertices = [] for edge in incoming_edges: sv = Edge.StartVertex(edge) incoming_vertices.append(Graph.NearestVertex(graph, sv)) return incoming_vertices @staticmethod def IsBipartite(graph, tolerance=0.0001): """ Returns True if the input graph is bipartite. Returns False otherwise. See https://en.wikipedia.org/wiki/Bipartite_graph. Parameters ---------- graph : topologic_core.Graph The input graph. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- bool True if the input graph is complete. False otherwise """ # From https://www.geeksforgeeks.org/bipartite-graph/ # This code is contributed by divyesh072019. from topologicpy.Topology import Topology def isBipartite(V, adj): # vector to store colour of vertex # assigning all to -1 i.e. uncoloured # colours are either 0 or 1 # for understanding take 0 as red and 1 as blue col = [-1]*(V) # queue for BFS storing {vertex , colour} q = [] #loop incase graph is not connected for i in range(V): # if not coloured if (col[i] == -1): # colouring with 0 i.e. red q.append([i, 0]) col[i] = 0 while len(q) != 0: p = q[0] q.pop(0) # current vertex v = p[0] # colour of current vertex c = p[1] # traversing vertexes connected to current vertex for j in adj[v]: # if already coloured with parent vertex color # then bipartite graph is not possible if (col[j] == c): return False # if uncoloured if (col[j] == -1): # colouring with opposite color to that of parent if c == 1: col[j] = 0 else: col[j] = 1 q.append([j, col[j]]) # if all vertexes are coloured such that # no two connected vertex have same colours return True if not Topology.IsInstance(graph, "Graph"): print("Graph.IsBipartite - Error: The input graph is not a valid graph. Returning None.") return None order = Graph.Order(graph) adjList = Graph.AdjacencyList(graph, tolerance=tolerance) return isBipartite(order, adjList) @staticmethod def IsComplete(graph): """ Returns True if the input graph is complete. Returns False otherwise. See https://en.wikipedia.org/wiki/Complete_graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- bool True if the input graph is complete. False otherwise """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IsComplete - Error: The input graph is not a valid graph. Returning None.") return None return graph.IsComplete() @staticmethod def IsErdoesGallai(graph, sequence): """ Returns True if the input sequence satisfies the Erdős–Gallai theorem. Returns False otherwise. See https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Gallai_theorem. Parameters ---------- graph : topologic_core.Graph The input graph. sequence : list The input sequence. Returns ------- bool True if the input sequence satisfies the Erdős–Gallai theorem. False otherwise. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IsErdoesGallai - Error: The input graph is not a valid graph. Returning None.") return None return graph.IsErdoesGallai(sequence) @staticmethod def IsolatedVertices(graph): """ Returns the list of isolated vertices in the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- list The list of isolated vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IsolatedVertices - Error: The input graph is not a valid graph. Returning None.") return None vertices = [] _ = graph.IsolatedVertices(vertices) return vertices @staticmethod def JSONData(graph, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", sourceKey="source", targetKey="target", xKey="x", yKey="y", zKey="z", geometryKey="brep", mantissa=6): """ Converts the input graph into JSON data. Parameters ---------- graph : topologic_core.Graph The input graph. verticesKey : str , optional The desired key name to call vertices. The default is "vertices". edgesKey : str , optional The desired key name to call edges. The default is "edges". vertexLabelKey : str , optional If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique. edgeLabelKey : str , optional If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique. sourceKey : str , optional The dictionary key used to store the source vertex. The default is "source". targetKey : str , optional The dictionary key used to store the target vertex. The default is "source". xKey : str , optional The desired key name to use for x-coordinates. The default is "x". yKey : str , optional The desired key name to use for y-coordinates. The default is "y". zKey : str , optional The desired key name to use for z-coordinates. The default is "z". geometryKey : str , optional The desired key name to use for geometry. The default is "brep". mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- dict The JSON data """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Helper import Helper vertices = Graph.Vertices(graph) j_data = {} j_data[verticesKey] = {} j_data[edgesKey] = {} n = max(len(str(len(vertices))), 4) v_labels = [] v_dicts = [] for i, v in enumerate(vertices): d = Topology.Dictionary(v) d = Dictionary.SetValueAtKey(d, xKey, Vertex.X(v, mantissa=mantissa)) d = Dictionary.SetValueAtKey(d, yKey, Vertex.Y(v, mantissa=mantissa)) d = Dictionary.SetValueAtKey(d, zKey, Vertex.Z(v, mantissa=mantissa)) if geometryKey: v_d = Topology.Dictionary(v) brep = Dictionary.ValueAtKey(v_d,"brep") if brep: d = Dictionary.SetValueAtKey(d, geometryKey, brep) v_dict = Dictionary.PythonDictionary(d) v_label = Dictionary.ValueAtKey(d, vertexLabelKey) if isinstance(v_label, str): v_label = Dictionary.ValueAtKey(d, vertexLabelKey) else: v_label = "Vertex_"+str(i).zfill(n) v_labels.append(v_label) v_dicts.append(v_dict) v_labels = Helper.MakeUnique(v_labels) for i, v_label in enumerate(v_labels): j_data[verticesKey][v_label] = v_dicts[i] edges = Graph.Edges(graph) n = len(str(len(edges))) e_labels = [] e_dicts = [] for i, e in enumerate(edges): sv = Edge.StartVertex(e) ev = Edge.EndVertex(e) svi = Vertex.Index(sv, vertices) evi = Vertex.Index(ev, vertices) sv_label = v_labels[svi] ev_label = v_labels[evi] d = Topology.Dictionary(e) d = Dictionary.SetValueAtKey(d, sourceKey, sv_label) d = Dictionary.SetValueAtKey(d, targetKey, ev_label) e_dict = Dictionary.PythonDictionary(d) e_label = Dictionary.ValueAtKey(d, edgeLabelKey) if isinstance(e_label, str): e_label = Dictionary.ValueAtKey(d, edgeLabelKey) else: e_label = "Edge_"+str(i).zfill(n) e_labels.append(e_label) e_dicts.append(e_dict) e_labels = Helper.MakeUnique(e_labels) for i, e_label in enumerate(e_labels): j_data[edgesKey][e_label] = e_dicts[i] return j_data @staticmethod def JSONString(graph, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", xKey = "x", yKey = "y", zKey = "z", indent=4, sortKeys=False, mantissa=6): """ Converts the input graph into JSON data. Parameters ---------- graph : topologic_core.Graph The input graph. verticesKey : str , optional The desired key name to call vertices. The default is "vertices". edgesKey : str , optional The desired key name to call edges. The default is "edges". vertexLabelKey : str , optional If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique. edgeLabelKey : str , optional If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique. xKey : str , optional The desired key name to use for x-coordinates. The default is "x". yKey : str , optional The desired key name to use for y-coordinates. The default is "y". zKey : str , optional The desired key name to use for z-coordinates. The default is "z". indent : int , optional The desired amount of indent spaces to use. The default is 4. sortKeys : bool , optional If set to True, the keys will be sorted. Otherwise, they won't be. The default is False. mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- str The JSON str """ import json json_data = Graph.JSONData(graph, verticesKey=verticesKey, edgesKey=edgesKey, vertexLabelKey=vertexLabelKey, edgeLabelKey=edgeLabelKey, xKey=xKey, yKey=yKey, zKey=zKey, mantissa=mantissa) json_string = json.dumps(json_data, indent=indent, sort_keys=sortKeys) return json_string @staticmethod def LocalClusteringCoefficient(graph, vertices=None, mantissa=6): """ Returns the local clustering coefficient of the input list of vertices within the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. If set to None, the local clustering coefficient of all vertices will be computed. mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- list The list of local clustering coefficient. The order of the list matches the order of the list of input vertices. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology def local_clustering_coefficient(adjacency_matrix, node): """ Compute the local clustering coefficient for a given node in a graph represented by an adjacency matrix. Parameters: - adjacency_matrix: 2D list representing the adjacency matrix of the graph - node: Node for which the local clustering coefficient is computed Returns: - Local clustering coefficient for the given node """ neighbors = [i for i, value in enumerate(adjacency_matrix[node]) if value == 1] num_neighbors = len(neighbors) if num_neighbors < 2: # If the node has less than 2 neighbors, the clustering coefficient is undefined return 0.0 # Count the number of connections between the neighbors num_connections = 0 for i in range(num_neighbors): for j in range(i + 1, num_neighbors): if adjacency_matrix[neighbors[i]][neighbors[j]] == 1: num_connections += 1 # Calculate the local clustering coefficient local_clustering_coeff = 2.0 * num_connections / (num_neighbors * (num_neighbors - 1)) return local_clustering_coeff if not Topology.IsInstance(graph, "Graph"): print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.") return None if vertices == None: vertices = Graph.Vertices(graph) if Topology.IsInstance(vertices, "Vertex"): vertices = [vertices] vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.LocalClusteringCoefficient - Error: The input vertices parameter does not contain valid vertices. Returning None.") return None g_vertices = Graph.Vertices(graph) adjacency_matrix = Graph.AdjacencyMatrix(graph) lcc = [] for v in vertices: i = Vertex.Index(v, g_vertices) if not i == None: lcc.append(round(local_clustering_coefficient(adjacency_matrix, i), mantissa)) else: lcc.append(None) return lcc @staticmethod def LongestPath(graph, vertexA, vertexB, vertexKey=None, edgeKey=None, costKey=None, timeLimit=10, tolerance=0.0001): """ Returns the longest path that connects the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. vertexKey : str , optional The vertex key to maximize. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value must be numeric. The default is None. edgeKey : str , optional The edge key to maximize. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length". costKey : str , optional If not None, the total cost of the longest_path will be stored in its dictionary under this key. The default is None. timeLimit : int , optional The time limit in second. The default is 10 seconds. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Wire The longest path between the input vertices. """ from topologicpy. Dictionary import Dictionary from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Wire import Wire from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology from topologicpy.Helper import Helper if not Topology.IsInstance(graph, "Graph"): print("Graph.LongestPath - Error: the input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.LongestPath - Error: the input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.LongestPath - Error: the input vertexB is not a valid vertex. Returning None.") return None g_edges = Graph.Edges(graph) paths = Graph.AllPaths(graph, vertexA, vertexB, timeLimit=timeLimit) if not paths: print("Graph.LongestPath - Error: Could not find any paths within the specified time limit. Returning None.") return None if len(paths) < 1: print("Graph.LongestPath - Error: Could not find any paths within the specified time limit. Returning None.") return None if edgeKey == None: lengths = [len(Topology.Edges(path)) for path in paths] elif edgeKey.lower() == "length": lengths = [Wire.Length(path) for path in paths] else: lengths = [] for path in paths: edges = Topology.Edges(path) pathCost = 0 for edge in edges: index = Edge.Index(edge, g_edges) d = Topology.Dictionary(g_edges[index]) value = Dictionary.ValueAtKey(d, edgeKey) if not value == None: pathCost += value lengths.append(pathCost) if not vertexKey == None: g_vertices = Graph.Vertices(graph) for i, path in enumerate(paths): vertices = Topology.Vertices(path) pathCost = 0 for vertex in vertices: index = Vertex.Index(vertex, g_vertices) d = Topology.Dictionary(g_vertices[index]) value = Dictionary.ValueAtKey(d, vertexKey) if not value == None: pathCost += value lengths[i] += pathCost new_paths = Helper.Sort(paths, lengths) temp_path = new_paths[-1] cost = lengths[-1] new_edges = [] for edge in Topology.Edges(temp_path): new_edges.append(g_edges[Edge.Index(edge, g_edges)]) longest_path = Topology.SelfMerge(Cluster.ByTopologies(new_edges), tolerance=tolerance) sv = Topology.Vertices(longest_path)[0] if Vertex.Distance(sv, vertexB) < tolerance: # Wire is reversed. Re-reverse it if Topology.IsInstance(longest_path, "Edge"): longest_path = Edge.Reverse(longest_path) elif Topology.IsInstance(longest_path, "Wire"): longest_path = Wire.Reverse(longest_path) longest_path = Wire.OrientEdges(longest_path, Wire.StartVertex(longest_path), tolerance=tolerance) if not costKey == None: lengths.sort() d = Dictionary.ByKeysValues([costKey], [cost]) longest_path = Topology.SetDictionary(longest_path, d) return longest_path @staticmethod def MaximumDelta(graph): """ Returns the maximum delta of the input graph. The maximum delta of a graph is the maximum degree of a vertex in the graph. Parameters ---------- graph : topologic_core.Graph the input graph. Returns ------- int The maximum delta. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.MaximumDelta - Error: The input graph is not a valid graph. Returning None.") return None return graph.MaximumDelta() @staticmethod def MaximumFlow(graph, source, sink, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=False, residualKey="residual", tolerance=0.0001): """ Returns the maximum flow of the input graph. See https://en.wikipedia.org/wiki/Maximum_flow_problem Parameters ---------- graph : topologic_core.Graph The input graph. This is assumed to be a directed graph source : topologic_core.Vertex The input source vertex. sink : topologic_core.Vertex The input sink/target vertex. edgeKeyFwd : str , optional The edge dictionary key to use to find the value of the forward capacity of the edge. If not set, the length of the edge is used as its capacity. The default is None. edgeKeyBwd : str , optional The edge dictionary key to use to find the value of the backward capacity of the edge. This is only considered if the edge is set to be bidrectional. The default is None. bidirKey : str , optional The edge dictionary key to use to determine if the edge is bidrectional. The default is None. bidrectional : bool , optional If set to True, the whole graph is considered to be bidirectional. The default is False. residualKey : str , optional The name of the key to use to store the residual value of each edge capacity in the input graph. The default is "residual". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- float The maximum flow. """ from topologicpy.Vertex import Vertex from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology # Using BFS as a searching algorithm def searching_algo_BFS(adjMatrix, s, t, parent): visited = [False] * (len(adjMatrix)) queue = [] queue.append(s) visited[s] = True while queue: u = queue.pop(0) for ind, val in enumerate(adjMatrix[u]): if visited[ind] == False and val > 0: queue.append(ind) visited[ind] = True parent[ind] = u return True if visited[t] else False # Applying fordfulkerson algorithm def ford_fulkerson(adjMatrix, source, sink): am = adjMatrix.copy() row = len(am) parent = [-1] * (row) max_flow = 0 while searching_algo_BFS(am, source, sink, parent): path_flow = float("Inf") s = sink while(s != source): path_flow = min(path_flow, am[parent[s]][s]) s = parent[s] # Adding the path flows max_flow += path_flow # Updating the residual values of edges v = sink while(v != source): u = parent[v] am[u][v] -= path_flow am[v][u] += path_flow v = parent[v] return [max_flow, am] if edgeKeyFwd == None: useEdgeLength = True else: useEdgeLength = False adjMatrix = Graph.AdjacencyMatrix(graph, edgeKeyFwd=edgeKeyFwd, edgeKeyBwd=edgeKeyBwd, bidirKey=bidirKey, bidirectional=bidirectional, useEdgeIndex = False, useEdgeLength=useEdgeLength, tolerance=tolerance) edgeMatrix = Graph.AdjacencyMatrix(graph, edgeKeyFwd=edgeKeyFwd, edgeKeyBwd=edgeKeyBwd, bidirKey=bidirKey, bidirectional=bidirectional, useEdgeIndex = True, useEdgeLength=False, tolerance=tolerance) vertices = Graph.Vertices(graph) edges = Graph.Edges(graph) sourceIndex = Vertex.Index(source, vertices) sinkIndex = Vertex.Index(sink, vertices) max_flow, am = ford_fulkerson(adjMatrix=adjMatrix, source=sourceIndex, sink=sinkIndex) for i in range(len(am)): row = am[i] for j in range(len(row)): residual = am[i][j] edge = edges[edgeMatrix[i][j]-1] d = Topology.Dictionary(edge) if not d == None: keys = Dictionary.Keys(d) values = Dictionary.Values(d) else: keys = [] values = [] keys.append(residualKey) values.append(residual) d = Dictionary.ByKeysValues(keys, values) edge = Topology.SetDictionary(edge, d) return max_flow @staticmethod def MeshData(g): """ Returns the mesh data of the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- dict The python dictionary of the mesh data of the input graph. The keys in the dictionary are: 'vertices' : The list of [x, y, z] coordinates of the vertices. 'edges' : the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j]. 'vertexDictionaries' : The python dictionaries of the vertices (in the same order as the list of vertices). 'edgeDictionaries' : The python dictionaries of the edges (in the same order as the list of edges). """ from topologicpy.Vertex import Vertex from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology g_vertices = Graph.Vertices(g) m_vertices = [] v_dicts = [] for g_vertex in g_vertices: m_vertices.append(Vertex.Coordinates(g_vertex)) d = Dictionary.PythonDictionary(Topology.Dictionary(g_vertex)) v_dicts.append(d) g_edges = Graph.Edges(g) m_edges = [] e_dicts = [] for g_edge in g_edges: sv = g_edge.StartVertex() ev = g_edge.EndVertex() si = Vertex.Index(sv, g_vertices) ei = Vertex.Index(ev, g_vertices) m_edges.append([si, ei]) d = Dictionary.PythonDictionary(Topology.Dictionary(g_edge)) e_dicts.append(d) return {'vertices':m_vertices, 'edges': m_edges, 'vertexDictionaries': v_dicts, 'edgeDictionaries': e_dicts } @staticmethod def MinimumDelta(graph): """ Returns the minimum delta of the input graph. The minimum delta of a graph is the minimum degree of a vertex in the graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The minimum delta. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.MinimumDelta - Error: The input graph is not a valid graph. Returning None.") return None return graph.MinimumDelta() @staticmethod def MinimumSpanningTree(graph, edgeKey=None, tolerance=0.0001): """ Returns the minimum spanning tree of the input graph. See https://en.wikipedia.org/wiki/Minimum_spanning_tree. Parameters ---------- graph : topologic_core.Graph The input graph. edgeKey : string , optional If set, the value of the edgeKey will be used as the weight and the tree will minimize the weight. The value associated with the edgeKey must be numerical. If the key is not set, the edges will be sorted by their length. The default is None tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The minimum spanning tree. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology def vertexInList(vertex, vertexList, tolerance=0.0001): for v in vertexList: if Vertex.Distance(v, vertex) < tolerance: return True return False if not Topology.IsInstance(graph, "Graph"): print("Graph.MinimumSpanningTree - Error: The input graph is not a valid graph. Returning None.") return None edges = Graph.Edges(graph) vertices = Graph.Vertices(graph) values = [] if isinstance(edgeKey, str): for edge in edges: d = Dictionary.Dictionary(edge) value = Dictionary.ValueAtKey(d, edgeKey) if value == None or not isinstance(value, int) or not isinstance(value, float): return None values.append(value) else: for edge in edges: value = Edge.Length(edge) values.append(value) keydict = dict(zip(edges, values)) edges.sort(key=keydict.get) mst = Graph.ByVerticesEdges(vertices,[]) for edge in edges: sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) if len(Graph.Vertices(mst)) > 0: if not Graph.Path(mst, Graph.NearestVertex(mst, sv), Graph.NearestVertex(mst, ev)): d = Topology.Dictionary(edge) if len(Dictionary.Keys(d)) > 0: tranEdgeDicts = True else: tranEdgeDicts = False mst = Graph.AddEdge(mst, edge, transferVertexDictionaries=False, transferEdgeDictionaries=tranEdgeDicts) return mst @staticmethod def NavigationGraph(face, sources=None, destinations=None, tolerance=0.0001, progressBar=True): """ Creates a 2D navigation graph. Parameters ---------- face : topologic_core.Face The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles sources : list The first input list of sources (vertices). Navigation edges will connect these veritces to destinations. destinations : list The input list of destinations (vertices). Navigation edges will connect these vertices to sources. tolerance : float , optional The desired tolerance. The default is 0.0001. tqdm : bool , optional If set to True, a tqdm progress bar is shown. Otherwise, it is not. The default is True. Returns ------- topologic_core.Graph The navigation graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Wire import Wire from topologicpy.Face import Face from topologicpy.Graph import Graph from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology import topologic_core as topologic from tqdm.auto import tqdm if not Topology.IsInstance(face, "Face"): print("Graph.NavigationGraph - Error: The input face parameter is not a valid face. Returning None") return None if sources == None: sources = Topology.Vertices(face) if destinations == None: destinations = Topology.Vertices(face) if not isinstance(sources, list): print("Graph.NavigationGraph - Error: The input sources parameter is not a valid list. Returning None") return None if not isinstance(destinations, list): print("Graph.NavigationGraph - Error: The input destinations parameter is not a valid list. Returning None") return None sources = [v for v in sources if Topology.IsInstance(v, "Vertex")] if len(sources) < 1: print("Graph.NavigationGraph - Error: The input sources parameter does not contain any vertices. Returning None") return None destinations = [v for v in destinations if Topology.IsInstance(v, "Vertex")] #if len(destinations) < 1: #Nothing to navigate to, so return a graph made of sources #return Graph.ByVerticesEdges(sources, []) # Add obstuse angles of external boundary to viewpoints e_boundary = Face.ExternalBoundary(face) if Topology.IsInstance(e_boundary, "Wire"): vertices = Topology.Vertices(e_boundary) interior_angles = Wire.InteriorAngles(e_boundary) for i, ang in enumerate(interior_angles): if ang > 180: sources.append(vertices[i]) destinations.append(vertices[i]) i_boundaries = Face.InternalBoundaries(face) for i_boundary in i_boundaries: if Topology.IsInstance(i_boundary, "Wire"): vertices = Topology.Vertices(i_boundary) interior_angles = Wire.InteriorAngles(i_boundary) for i, ang in enumerate(interior_angles): if ang < 180: sources.append(vertices[i]) destinations.append(vertices[i]) used = [] for i in range(max(len(sources), len(destinations))): temp_row = [] for j in range(max(len(sources), len(destinations))): temp_row.append(0) used.append(temp_row) final_edges = [] if progressBar: the_range = tqdm(range(len(sources))) else: the_range = range(len(sources)) for i in the_range: source = sources[i] index_b = Vertex.Index(source, destinations) for j in range(len(destinations)): destination = destinations[j] index_a = Vertex.Index(destination, sources) if used[i][j] == 1 or used[j][i]: continue if Vertex.Distance(source, destination) > tolerance: edge = Edge.ByVertices([source,destination]) e = Topology.Boolean(edge, face, operation="intersect") if Topology.IsInstance(e, "Edge"): final_edges.append(edge) used[i][j] = 1 if not index_a == None and not index_b == None: used[j][i] = 1 if len(i_boundaries) > 0: holes_edges = Topology.Edges(Cluster.ByTopologies(i_boundaries)) final_edges += holes_edges if len(final_edges) > 0: final_vertices = Topology.Vertices(Cluster.ByTopologies(final_edges)) g = Graph.ByVerticesEdges(final_vertices, final_edges) return g return None @staticmethod def NearestVertex(graph, vertex): """ Returns the vertex in the input graph that is the nearest to the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. Returns ------- topologic_core.Vertex The vertex in the input graph that is the nearest to the input vertex. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.NearestVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.NearestVertex - Error: The input vertex is not a valid vertex. Returning None.") return None vertices = Graph.Vertices(graph) nearestVertex = vertices[0] nearestDistance = Vertex.Distance(vertex, nearestVertex) for aGraphVertex in vertices: newDistance = Vertex.Distance(vertex, aGraphVertex) if newDistance < nearestDistance: nearestDistance = newDistance nearestVertex = aGraphVertex return nearestVertex @staticmethod def NetworkXGraph(graph, tolerance=0.0001): """ converts the input graph into a NetworkX Graph. See http://networkx.org Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- networkX Graph The created networkX Graph """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary try: import networkx as nx except: print("Graph.NetworkXGraph - Information: Installing required networkx library.") try: os.system("pip install networkx") except: os.system("pip install networkx --user") try: import networkx as nx print("Graph.NetworkXGraph - Infromation: networkx library installed correctly.") except: warnings.warn("Graph - Error: Could not import networkx. Please try to install networkx manually. Returning None.") return None if not Topology.IsInstance(graph, "Graph"): print("Graph.NetworkXGraph - Error: The input graph is not a valid graph. Returning None.") return None nxGraph = nx.Graph() vertices = Graph.Vertices(graph) order = len(vertices) nodes = [] for i in range(order): v = vertices[i] d = Topology.Dictionary(vertices[i]) if d: keys = Dictionary.Keys(d) if not keys: keys = [] values = Dictionary.Values(d) if not values: values = [] keys += ["x","y","z"] import random values += [Vertex.X(v), Vertex.Y(v), Vertex.Z(v)] d = Dictionary.ByKeysValues(keys,values) pythonD = Dictionary.PythonDictionary(d) nodes.append((i, pythonD)) else: nodes.append((i, {"name": str(i)})) nxGraph.add_nodes_from(nodes) for i in range(order): v = vertices[i] adjVertices = Graph.AdjacentVertices(graph, vertices[i]) for adjVertex in adjVertices: adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance) if not adjIndex == None: nxGraph.add_edge(i,adjIndex, length=(Vertex.Distance(v, adjVertex))) pos=nx.spring_layout(nxGraph, k=0.2) nx.set_node_attributes(nxGraph, pos, "pos") return nxGraph @staticmethod def Order(graph): """ Returns the graph order of the input graph. The graph order is its number of vertices. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The number of vertices in the input graph """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Order - Error: The input graph is not a valid graph. Returning None.") return None return len(Graph.Vertices(graph)) @staticmethod def OutgoingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the outgoing edges connected to a vertex. An edge is considered outgoing if its start vertex is coincident with the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of outgoing edges """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IncomingEdges - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.IncomingEdges - Error: The input vertex parameter is not a valid vertex. Returning None.") return None edges = Graph.Edges(graph, [vertex]) if directed == False: return edges outgoing_edges = [] for edge in edges: sv = Edge.StartVertex(edge) if Vertex.Distance(vertex, sv) < tolerance: outgoing_edges.append(edge) return outgoing_edges @staticmethod def OutgoingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the list of outgoing vertices connected to a vertex. A vertex is considered outgoing if it is an adjacent vertex to the input vertex and the the edge connecting it to the input vertex is an outgoing edge. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of incoming vertices """ from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.OutgoingVertices - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.OutgoingVertices - Error: The input vertex parameter is not a valid vertex. Returning None.") return None if directed == False: return Graph.AdjacentVertices(graph, vertex) outgoing_edges = Graph.OutgoingEdges(graph, vertex, directed=directed, tolerance=tolerance) outgoing_vertices = [] for edge in outgoing_edges: ev = Edge.EndVertex(edge) outgoing_vertices.append(Graph.NearestVertex(graph, ev)) return outgoing_vertices @staticmethod def PageRank(graph, alpha=0.85, maxIterations=100, normalize=True, directed=False, mantissa=6, tolerance=0.0001): """ Calculates PageRank scores for nodes in a directed graph. see https://en.wikipedia.org/wiki/PageRank. Parameters ---------- graph : topologic_core.Graph The input graph. alpha : float , optional The damping (dampening) factor. The default is 0.85. See https://en.wikipedia.org/wiki/PageRank. maxIterations : int , optional The maximum number of iterations to calculate the page rank. The default is 100. normalize : bool , optional If set to True, the results will be normalized from 0 to 1. Otherwise, they won't be. The default is True. directed : bool , optional If set to True, the graph is considered as a directed graph. Otherwise, it will be considered as an undirected graph. The default is False. mantissa : int , optional The desired length of the mantissa. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of page ranks for the vertices in the graph. """ from topologicpy.Vertex import Vertex from topologicpy.Helper import Helper vertices = Graph.Vertices(graph) num_vertices = len(vertices) if num_vertices < 1: print("Graph.PageRank - Error: The input graph parameter has no vertices. Returning None") return None initial_score = 1.0 / num_vertices scores = [initial_score for vertex in vertices] for _ in range(maxIterations): new_scores = [0 for vertex in vertices] for i, vertex in enumerate(vertices): incoming_score = 0 for incoming_vertex in Graph.IncomingVertices(graph, vertex, directed=directed): if len(Graph.IncomingVertices(graph, incoming_vertex, directed=directed)) > 0: incoming_score += scores[Vertex.Index(incoming_vertex, vertices)] / len(Graph.IncomingVertices(graph, incoming_vertex, directed=directed)) new_scores[i] = alpha * incoming_score + (1 - alpha) / num_vertices # Check for convergence if all(abs(new_scores[i] - scores[i]) < tolerance for i in range(len(vertices))): break scores = new_scores if normalize == True: scores = Helper.Normalize(scores, mantissa=mantissa) else: scores = [round(x, mantissa) for x in scores] return scores @staticmethod def Path(graph, vertexA, vertexB, tolerance=0.0001): """ Returns a path (wire) in the input graph that connects the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. tolerance : float, optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Wire The path (wire) in the input graph that connects the input vertices. """ from topologicpy.Wire import Wire from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Path - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.Path - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.Path - Error: The input vertexB is not a valid vertex. Returning None.") return None path = graph.Path(vertexA, vertexB) if Topology.IsInstance(path, "Wire"): path = Wire.OrientEdges(path, Wire.StartVertex(path), tolerance=tolerance) return path @staticmethod def PyvisGraph(graph, path, overwrite=True, height=900, backgroundColor="white", fontColor="black", notebook=False, vertexSize=6, vertexSizeKey=None, vertexColor="black", vertexColorKey=None, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=None, minVertexGroup=None, maxVertexGroup=None, edgeLabelKey=None, edgeWeight=0, edgeWeightKey=None, showNeighbours=True, selectMenu=True, filterMenu=True, colorScale="viridis"): """ Displays a pyvis graph. See https://pyvis.readthedocs.io/. Parameters ---------- graph : topologic_core.Graph The input graph. path : str The desired file path to the HTML file into which to save the pyvis graph. overwrite : bool , optional If set to True, the HTML file is overwritten. height : int , optional The desired figure height in pixels. The default is 900 pixels. backgroundColor : str, optional The desired background color for the figure. This can be a named color or a hexadecimal value. The default is 'white'. fontColor : str , optional The desired font color for the figure. This can be a named color or a hexadecimal value. The default is 'black'. notebook : bool , optional If set to True, the figure will be targeted at a Jupyter Notebook. Note that this is not working well. Pyvis has bugs. The default is False. vertexSize : int , optional The desired default vertex size. The default is 6. vertexSizeKey : str , optional If not set to None, the vertex size will be derived from the dictionary value set at this key. If set to "degree", the size of the vertex will be determined by its degree (number of neighbors). The default is None. vertexColor : int , optional The desired default vertex color. his can be a named color or a hexadecimal value. The default is 'black'. vertexColorKey : str , optional If not set to None, the vertex color will be derived from the dictionary value set at this key. The default is None. vertexLabelKey : str , optional If not set to None, the vertex label will be derived from the dictionary value set at this key. The default is None. vertexGroupKey : str , optional If not set to None, the vertex color will be determined by the group the vertex belongs to as derived from the value set at this key. The default is None. vertexGroups : list , optional The list of all possible vertex groups. This will help in vertex coloring. The default is None. minVertexGroup : int or float , optional If the vertex groups are numeric, specify the minimum value you wish to consider for vertex coloring. The default is None. maxVertexGroup : int or float , optional If the vertex groups are numeric, specify the maximum value you wish to consider for vertex coloring. The default is None. edgeWeight : int , optional The desired default weight of the edge. This determines its thickness. The default is 0. edgeWeightKey : str, optional If not set to None, the edge weight will be derived from the dictionary value set at this key. If set to "length" or "distance", the weight of the edge will be determined by its geometric length. The default is None. edgeLabelKey : str , optional If not set to None, the edge label will be derived from the dictionary value set at this key. The default is None. showNeighbors : bool , optional If set to True, a list of neighbors is shown when you hover over a vertex. The default is True. selectMenu : bool , optional If set to True, a selection menu will be displayed. The default is True filterMenu : bool , optional If set to True, a filtering menu will be displayed. The default is True. colorScale : str , optional The desired type of plotly color scales to use (e.g. "viridis", "plasma"). The default is "viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/. Returns ------- None The pyvis graph is displayed either inline (notebook mode) or in a new browser window or tab. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Color import Color from os.path import exists try: from pyvis.network import Network except: print("Graph.PyvisGraph - Information: Installing required pyvis library.") try: os.system("pip install pyvis") except: os.system("pip install pyvis --user") try: from pyvis.network import Network print("Graph.PyvisGraph - Information: pyvis library installed correctly.") except: warnings.warn("Graph - Error: Could not import pyvis. Please try to install pyvis manually. Retruning None.") return None net = Network(height=str(height)+"px", width="100%", bgcolor=backgroundColor, font_color=fontColor, select_menu=selectMenu, filter_menu=filterMenu, cdn_resources="remote", notebook=notebook) if notebook == True: net.prep_notebook() vertices = Graph.Vertices(graph) edges = Graph.Edges(graph) nodes = [i for i in range(len(vertices))] if not vertexLabelKey == None: node_labels = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexLabelKey) for v in vertices] else: node_labels = list(range(len(vertices))) if not vertexColorKey == None: colors = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexColorKey) for v in vertices] else: colors = [vertexColor for v in vertices] node_titles = [str(n) for n in node_labels] group = "" if not vertexGroupKey == None: colors = [] if vertexGroups: if len(vertexGroups) > 0: if type(vertexGroups[0]) == int or type(vertexGroups[0]) == float: if not minVertexGroup: minVertexGroup = min(vertexGroups) if not maxVertexGroup: maxVertexGroup = max(vertexGroups) else: minVertexGroup = 0 maxVertexGroup = len(vertexGroups) - 1 else: minVertexGroup = 0 maxVertexGroup = 1 for m, v in enumerate(vertices): group = "" d = Topology.Dictionary(v) if d: try: group = Dictionary.ValueAtKey(d, key=vertexGroupKey) or None except: group = "" try: if type(group) == int or type(group) == float: if group < minVertexGroup: group = minVertexGroup if group > maxVertexGroup: group = maxVertexGroup color = Color.RGBToHex(Color.ByValueInRange(group, minValue=minVertexGroup, maxValue=maxVertexGroup, colorScale=colorScale)) else: color = Color.RGBToHex(Color.ByValueInRange(vertexGroups.index(group), minValue=minVertexGroup, maxValue=maxVertexGroup, colorScale=colorScale)) colors.append(color) except: colors.append(vertexColor) net.add_nodes(nodes, label=node_labels, title=node_titles, color=colors) for e in edges: edge_label = "" if not edgeLabelKey == None: d = Topology.Dictionary(e) edge_label = Dictionary.ValueAtKey(d, edgeLabelKey) if edge_label == None: edge_label = "" w = edgeWeight if not edgeWeightKey == None: d = Topology.Dictionary(e) if edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance": w = Edge.Length(e) else: weightValue = Dictionary.ValueAtKey(d, edgeWeightKey) if weightValue: w = weightValue sv = Edge.StartVertex(e) ev = Edge.EndVertex(e) svi = Vertex.Index(sv, vertices) evi = Vertex.Index(ev, vertices) net.add_edge(svi, evi, weight=w, label=edge_label) net.inherit_edge_colors(False) # add neighbor data to node hover data and compute vertexSize if showNeighbours == True or not vertexSizeKey == None: for i, node in enumerate(net.nodes): if showNeighbours == True: neighbors = list(net.neighbors(node["id"])) neighbor_labels = [str(net.nodes[n]["id"])+": "+str(net.nodes[n]["label"]) for n in neighbors] node["title"] = str(node["id"])+": "+node["title"]+"\n" node["title"] += "Neighbors:\n" + "\n".join(neighbor_labels) vs = vertexSize if not vertexSizeKey == None: d = Topology.Dictionary(vertices[i]) if vertexSizeKey.lower() == "neighbours" or vertexSizeKey.lower() == "degree": temp_vs = Graph.VertexDegree(graph, vertices[i]) else: temp_vs = Dictionary.ValueAtKey(vertices[i], vertexSizeKey) if temp_vs: vs = temp_vs node["value"] = vs # Make sure the file extension is .html ext = path[len(path)-5:len(path)] if ext.lower() != ".html": path = path+".html" if not overwrite and exists(path): print("Graph.PyvisGraph - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None if overwrite == True: net.save_graph(path) net.show_buttons() net.show(path, notebook=notebook) return None @staticmethod def RemoveEdge(graph, edge, tolerance=0.0001): """ Removes the input edge from the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input edge. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input edge removed. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.RemoveEdge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.RemoveEdge - Error: The input edge is not a valid edge. Returning None.") return None _ = graph.RemoveEdges([edge], tolerance) return graph @staticmethod def RemoveVertex(graph, vertex, tolerance=0.0001): """ Removes the input vertex from the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input vertex removed. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.RemoveVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.RemoveVertex - Error: The input vertex is not a valid vertex. Returning None.") return None graphVertex = Graph.NearestVertex(graph, vertex) _ = graph.RemoveVertices([graphVertex]) return graph @staticmethod def SetDictionary(graph, dictionary): """ Sets the input graph's dictionary to the input dictionary Parameters ---------- graph : topologic_core.Graph The input graph. dictionary : topologic_core.Dictionary or dict The input dictionary. Returns ------- topologic_core.Graph The input graph with the input dictionary set in it. """ from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.SetDictionary - Error: the input graph parameter is not a valid graph. Returning None.") return None if isinstance(dictionary, dict): dictionary = Dictionary.ByPythonDictionary(dictionary) if not Topology.IsInstance(dictionary, "Dictionary"): print("Graph.SetDictionary - Warning: the input dictionary parameter is not a valid dictionary. Returning original input.") return graph if len(dictionary.Keys()) < 1: print("Graph.SetDictionary - Warning: the input dictionary parameter is empty. Returning original input.") return graph _ = graph.SetDictionary(dictionary) return graph @staticmethod def ShortestPath(graph, vertexA, vertexB, vertexKey="", edgeKey="Length", tolerance=0.0001): """ Returns the shortest path that connects the input vertices. The shortest path will take into consideration both the vertexKey and the edgeKey if both are specified and will minimize the total "cost" of the path. Otherwise, it will take into consideration only whatever key is specified. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. vertexKey : string , optional The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None. edgeKey : string , optional The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Wire The shortest path between the input vertices. """ from topologicpy.Vertex import Vertex from topologicpy.Wire import Wire from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ShortestPath - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.ShortestPath - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.ShortestPath - Error: The input vertexB is not a valid vertex. Returning None.") return None if edgeKey: if edgeKey.lower() == "length": edgeKey = "Length" try: gsv = Graph.NearestVertex(graph, vertexA) gev = Graph.NearestVertex(graph, vertexB) shortest_path = graph.ShortestPath(gsv, gev, vertexKey, edgeKey) if Topology.IsInstance(shortest_path, "Edge"): shortest_path = Wire.ByEdges([shortest_path]) sv = Topology.Vertices(shortest_path)[0] if Vertex.Distance(sv, gev) < tolerance: # Path is reversed. Correct it. if Topology.IsInstance(shortest_path, "Wire"): shortest_path = Wire.Reverse(shortest_path) shortest_path = Wire.OrientEdges(shortest_path, Wire.StartVertex(shortest_path), tolerance=tolerance) return shortest_path except: return None @staticmethod def ShortestPaths(graph, vertexA, vertexB, vertexKey="", edgeKey="length", timeLimit=10, pathLimit=10, tolerance=0.0001): """ Returns the shortest path that connects the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. vertexKey : string , optional The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None. edgeKey : string , optional The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length". timeLimit : int , optional The search time limit in seconds. The default is 10 seconds pathLimit: int , optional The number of found paths limit. The default is 10 paths. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of shortest paths between the input vertices. """ from topologicpy.Topology import Topology def isUnique(paths, path): if path == None: return False if len(paths) < 1: return True for aPath in paths: copyPath = topologic.Topology.DeepCopy(aPath) # Hook to Core dif = copyPath.Difference(path, False) if dif == None: return False return True if not Topology.IsInstance(graph, "Graph"): print("Graph.ShortestPaths - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.ShortestPaths - Error: The input vertexA parameter is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.ShortestPaths - Error: The input vertexB parameter is not a valid vertex. Returning None.") return None shortestPaths = [] end = time.time() + timeLimit while time.time() < end and len(shortestPaths) < pathLimit: if (graph != None): if edgeKey: if edgeKey.lower() == "length": edgeKey = "Length" shortest_path = Graph.ShortestPath(graph, vertexA, vertexB, vertexKey=vertexKey, edgeKey=edgeKey, tolerance=tolerance) # Find the first shortest path if isUnique(shortestPaths, shortest_path): shortestPaths.append(shortest_path) vertices = Graph.Vertices(graph) random.shuffle(vertices) edges = Graph.Edges(graph) graph = Graph.ByVerticesEdges(vertices, edges) return shortestPaths @staticmethod def Show(graph, vertexColor="black", vertexSize=6, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=[], showVertices=True, showVertexLegend=False, edgeColor="black", edgeWidth=1, edgeLabelKey=None, edgeGroupKey=None, edgeGroups=[], showEdges=True, showEdgeLegend=False, colorScale='viridis', renderer=None, width=950, height=500, xAxis=False, yAxis=False, zAxis=False, axisSize=1, backgroundColor='rgba(0,0,0,0)', marginLeft=0, marginRight=0, marginTop=20, marginBottom=0, camera=[-1.25, -1.25, 1.25], center=[0, 0, 0], up=[0, 0, 1], projection="perspective", tolerance=0.0001): """ Shows the graph using Plotly. Parameters ---------- graph : topologic_core.Graph The input graph. vertexColor : str , optional The desired color of the output vertices. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "black". vertexSize : float , optional The desired size of the vertices. The default is 1.1. vertexLabelKey : str , optional The dictionary key to use to display the vertex label. The default is None. vertexGroupKey : str , optional The dictionary key to use to display the vertex group. The default is None. vertexGroups : list , optional The list of vertex groups against which to index the color of the vertex. The default is []. showVertices : bool , optional If set to True the vertices will be drawn. Otherwise, they will not be drawn. The default is True. showVertexLegend : bool , optional If set to True the vertex legend will be drawn. Otherwise, it will not be drawn. The default is False. edgeColor : str , optional The desired color of the output edges. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "black". edgeWidth : float , optional The desired thickness of the output edges. The default is 1. edgeLabelKey : str , optional The dictionary key to use to display the edge label. The default is None. edgeGroupKey : str , optional The dictionary key to use to display the edge group. The default is None. showEdges : bool , optional If set to True the edges will be drawn. Otherwise, they will not be drawn. The default is True. showEdgeLegend : bool , optional If set to True the edge legend will be drawn. Otherwise, it will not be drawn. The default is False. colorScale : str , optional The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/. renderer : str , optional The desired renderer. See Plotly.Renderers(). If set to None, the code will attempt to discover the most suitable renderer. The default is None. width : int , optional The width in pixels of the figure. The default value is 950. height : int , optional The height in pixels of the figure. The default value is 950. xAxis : bool , optional If set to True the x axis is drawn. Otherwise it is not drawn. The default is False. yAxis : bool , optional If set to True the y axis is drawn. Otherwise it is not drawn. The default is False. zAxis : bool , optional If set to True the z axis is drawn. Otherwise it is not drawn. The default is False. axisSize : float , optional The size of the X, Y, Z, axes. The default is 1. backgroundColor : str , optional The desired color of the background. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "rgba(0,0,0,0)". marginLeft : int , optional The size in pixels of the left margin. The default value is 0. marginRight : int , optional The size in pixels of the right margin. The default value is 0. marginTop : int , optional The size in pixels of the top margin. The default value is 20. marginBottom : int , optional The size in pixels of the bottom margin. The default value is 0. camera : list , optional The desired location of the camera). The default is [-1.25, -1.25, 1.25]. center : list , optional The desired center (camera target). The default is [0, 0, 0]. up : list , optional The desired up vector. The default is [0, 0, 1]. projection : str , optional The desired type of projection. The options are "orthographic" or "perspective". It is case insensitive. The default is "perspective" tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- None """ from topologicpy.Plotly import Plotly from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Show - Error: The input graph is not a valid graph. Returning None.") return None data= Plotly.DataByGraph(graph, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLegend=showEdgeLegend, colorScale=colorScale) fig = Plotly.FigureByData(data, width=width, height=height, xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize, backgroundColor=backgroundColor, marginLeft=marginLeft, marginRight=marginRight, marginTop=marginTop, marginBottom=marginBottom, tolerance=tolerance) Plotly.Show(fig, renderer=renderer, camera=camera, center=center, up=up, projection=projection) @staticmethod def Size(graph): """ Returns the graph size of the input graph. The graph size is its number of edges. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The number of edges in the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Size - Error: The input graph is not a valid graph. Returning None.") return None return len(Graph.Edges(graph)) @staticmethod def TopologicalDistance(graph, vertexA, vertexB, tolerance=0.0001): """ Returns the topological distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory). Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- int The topological distance between the input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.TopologicalDistance - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.TopologicalDistance - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.TopologicalDistance - Error: The input vertexB is not a valid vertex. Returning None.") return None return graph.TopologicalDistance(vertexA, vertexB, tolerance) @staticmethod def Topology(graph): """ Returns the topology (cluster) of the input graph Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- topologic_core.Cluster The topology of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Topology - Error: The input graph is not a valid graph. Returning None.") return None return graph.Topology() @staticmethod def Tree(graph, vertex=None, tolerance=0.0001): """ Creates a tree graph version of the input graph rooted at the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex , optional The input root vertex. If not set, the first vertex in the graph is set as the root vertex. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The tree graph version of the input graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology def vertexInList(vertex, vertexList): if vertex and vertexList: if Topology.IsInstance(vertex, "Vertex") and isinstance(vertexList, list): for i in range(len(vertexList)): if vertexList[i]: if Topology.IsInstance(vertexList[i], "Vertex"): if Topology.IsSame(vertex, vertexList[i]): return True return False def getChildren(vertex, parent, graph, vertices): children = [] adjVertices = [] if vertex: adjVertices = Graph.AdjacentVertices(graph, vertex) if parent == None: return adjVertices else: for aVertex in adjVertices: if (not vertexInList(aVertex, [parent])) and (not vertexInList(aVertex, vertices)): children.append(aVertex) return children def buildTree(graph, dictionary, vertex, parent, tolerance=0.0001): vertices = dictionary['vertices'] edges = dictionary['edges'] if not vertexInList(vertex, vertices): vertices.append(vertex) if parent: edge = Graph.Edge(graph, parent, vertex, tolerance) ev = Edge.EndVertex(edge) if Vertex.Distance(parent, ev) < tolerance: edge = Edge.Reverse(edge) edges.append(edge) if parent == None: parent = vertex children = getChildren(vertex, parent, graph, vertices) dictionary['vertices'] = vertices dictionary['edges'] = edges for child in children: dictionary = buildTree(graph, dictionary, child, vertex, tolerance) return dictionary if not Topology.IsInstance(graph, "Graph"): print("Graph.Tree - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): vertex = Graph.Vertices(graph)[0] else: vertex = Graph.NearestVertex(graph, vertex) dictionary = {'vertices':[], 'edges':[]} dictionary = buildTree(graph, dictionary, vertex, None, tolerance) return Graph.ByVerticesEdges(dictionary['vertices'], dictionary['edges']) @staticmethod def VertexDegree(graph, vertex, edgeKey: str = None, tolerance: float = 0.0001): """ Returns the degree of the input vertex. See https://en.wikipedia.org/wiki/Degree_(graph_theory). Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. edgeKey : str , optional If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- int The degree of the input vertex. """ from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary import numbers if not Topology.IsInstance(graph, "Graph"): print("Graph.VertexDegree - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.VertexDegree - Error: The input vertex is not a valid vertex. Returning None.") return None if not isinstance(edgeKey, str): edgeKey = "" edges = Graph.Edges(graph, [vertex], tolerance=tolerance) degree = 0 for edge in edges: d = Topology.Dictionary(edge) value = Dictionary.ValueAtKey(d, edgeKey) if isinstance(value, numbers.Number): degree += value else: degree += 1 return degree @staticmethod def Vertices(graph, vertexKey=None, reverse=False): """ Returns the list of vertices in the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertexKey : str , optional If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None. reverse : bool , optional If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False. Returns ------- list The list of vertices in the input graph. """ from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Vertices - Error: The input graph is not a valid graph. Returning None.") return None vertices = [] if graph: try: _ = graph.Vertices(vertices) except: vertices = [] if not vertexKey == None: sorting_values = [] for v in vertices: d = Topology.Dictionary(v) value = Dictionary.ValueAtKey(d, vertexKey) sorting_values.append(value) vertices = Helper.Sort(vertices, sorting_values) if reverse == True: vertices.reverse() return vertices @staticmethod def VisibilityGraph(face, viewpointsA=None, viewpointsB=None, tolerance=0.0001): """ Creates a 2D visibility graph. Parameters ---------- face : topologic_core.Face The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles viewpointsA : list , optional The first input list of viewpoints (vertices). Visibility edges will connect these veritces to viewpointsB. If set to None, this parameters will be set to all vertices of the input face. The default is None. viewpointsB : list , optional The input list of viewpoints (vertices). Visibility edges will connect these vertices to viewpointsA. If set to None, this parameters will be set to all vertices of the input face. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The visibility graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Face import Face from topologicpy.Graph import Graph from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology if not Topology.IsInstance(face, "Face"): print("Graph.VisibilityGraph - Error: The input face parameter is not a valid face. Returning None") return None if viewpointsA == None: viewpointsA = Topology.Vertices(face) if viewpointsB == None: viewpointsB = Topology.Vertices(face) if not isinstance(viewpointsA, list): print("Graph.VisibilityGraph - Error: The input viewpointsA parameter is not a valid list. Returning None") return None if not isinstance(viewpointsB, list): print("Graph.VisibilityGraph - Error: The input viewpointsB parameter is not a valid list. Returning None") return None viewpointsA = [v for v in viewpointsA if Topology.IsInstance(v, "Vertex")] if len(viewpointsA) < 1: print("Graph.VisibilityGraph - Error: The input viewpointsA parameter does not contain any vertices. Returning None") return None viewpointsB = [v for v in viewpointsB if Topology.IsInstance(v, "Vertex")] if len(viewpointsB) < 1: #Nothing to look at, so return a graph made of viewpointsA return Graph.ByVerticesEdges(viewpointsA, []) i_boundaries = Face.InternalBoundaries(face) obstacles = [] for i_boundary in i_boundaries: if Topology.IsInstance(i_boundary, "Wire"): obstacles.append(Face.ByWire(i_boundary)) if len(obstacles) > 0: obstacle_cluster = Cluster.ByTopologies(obstacles) else: obstacle_cluster = None def intersects_obstacles(edge, obstacle_cluster, tolerance=0.0001): result = Topology.Difference(edge, obstacle_cluster) if result == None: return True if Topology.IsInstance(result, "Cluster"): return True if Topology.IsInstance(result, "Edge"): if abs(Edge.Length(edge) - Edge.Length(result)) > tolerance: return True return False final_edges = [] for i in tqdm(range(len(viewpointsA))): va = viewpointsA[i] for j in range(len(viewpointsB)): vb = viewpointsB[j] if Vertex.Distance(va, vb) > tolerance: edge = Edge.ByVertices([va,vb]) if not intersects_obstacles(edge, obstacle_cluster): final_edges.append(edge) if len(final_edges) > 0: final_vertices = Topology.Vertices(Cluster.ByTopologies(final_edges)) g = Graph.ByVerticesEdges(final_vertices, final_edges) return g return None
Static methods
def AddEdge(graph, edge, transferVertexDictionaries=False, transferEdgeDictionaries=False, tolerance=0.0001)
-
Adds the input edge to the input Graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
edge
:topologic_core.Edge
- The input edge.
transferDictionaries
:bool
, optional- If set to True, the dictionaries of the edge and its vertices are transfered to the graph.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph with the input edge added to it.
Expand source code
@staticmethod def AddEdge(graph, edge, transferVertexDictionaries=False, transferEdgeDictionaries=False, tolerance=0.0001): """ Adds the input edge to the input Graph. Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input edge. transferDictionaries : bool, optional If set to True, the dictionaries of the edge and its vertices are transfered to the graph. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input edge added to it. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology def addIfUnique(graph_vertices, vertex, tolerance): unique = True returnVertex = vertex for gv in graph_vertices: if (Vertex.Distance(vertex, gv) < tolerance): if transferVertexDictionaries == True: gd = Topology.Dictionary(gv) vd = Topology.Dictionary(vertex) gk = gd.Keys() vk = vd.Keys() d = None if (len(gk) > 0) and (len(vk) > 0): d = Dictionary.ByMergedDictionaries([gd, vd]) elif (len(gk) > 0) and (len(vk) < 1): d = gd elif (len(gk) < 1) and (len(vk) > 0): d = vd if d: _ = Topology.SetDictionary(gv, d) unique = False returnVertex = gv break if unique: graph_vertices.append(vertex) return [graph_vertices, returnVertex] if not Topology.IsInstance(graph, "Graph"): print("Graph.AddEdge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.AddEdge - Error: The input edge is not a valid edge. Returning None.") return None graph_vertices = Graph.Vertices(graph) graph_edges = Graph.Edges(graph, graph_vertices, tolerance) vertices = Edge.Vertices(edge) new_vertices = [] for vertex in vertices: graph_vertices, nv = addIfUnique(graph_vertices, vertex, tolerance) new_vertices.append(nv) new_edge = Edge.ByVertices([new_vertices[0], new_vertices[1]], tolerance=tolerance) if transferEdgeDictionaries == True: d = Topology.Dictionary(edge) keys = Dictionary.Keys(d) if isinstance(keys, list): if len(keys) > 0: _ = Topology.SetDictionary(new_edge, d) graph_edges.append(new_edge) new_graph = Graph.ByVerticesEdges(graph_vertices, graph_edges) return new_graph
def AddVertex(graph, vertex, tolerance=0.0001)
-
Adds the input vertex to the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph with the input vertex added to it.
Expand source code
@staticmethod def AddVertex(graph, vertex, tolerance=0.0001): """ Adds the input vertex to the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input vertex added to it. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AddVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.AddVertex - Error: The input vertex is not a valid vertex. Returning None.") return None _ = graph.AddVertices([vertex], tolerance) return graph
def AddVertices(graph, vertices, tolerance=0.0001)
-
Adds the input vertex to the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertices
:list
- The input list of vertices.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph with the input vertex added to it.
Expand source code
@staticmethod def AddVertices(graph, vertices, tolerance=0.0001): """ Adds the input vertex to the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list The input list of vertices. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input vertex added to it. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AddVertices - Error: The input graph is not a valid graph. Returning None.") return None if not isinstance(vertices, list): print("Graph.AddVertices - Error: The input list of vertices is not a valid list. Returning None.") return None vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.AddVertices - Error: Could not find any valid vertices in the input list of vertices. Returning None.") return None _ = graph.AddVertices(vertices, tolerance) return graph
def AdjacencyList(graph, vertexKey=None, reverse=True, tolerance=0.0001)
-
Returns the adjacency list of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_list.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexKey
:str
, optional- If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None.
reverse
:bool
, optional- If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The adjacency list.
Expand source code
@staticmethod def AdjacencyList(graph, vertexKey=None, reverse=True, tolerance=0.0001): """ Returns the adjacency list of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_list. Parameters ---------- graph : topologic_core.Graph The input graph. vertexKey : str , optional If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None. reverse : bool , optional If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The adjacency list. """ from topologicpy.Vertex import Vertex from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology from topologicpy.Helper import Helper if not Topology.IsInstance(graph, "Graph"): print("Graph.AdjacencyList - Error: The input graph is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) if not vertexKey == None: sorting_values = [] for v in vertices: d = Topology.Dictionary(v) value = Dictionary.ValueAtKey(d, vertexKey) sorting_values.append(value) vertices = Helper.Sort(vertices, sorting_values) if reverse == True: vertices.reverse() order = len(vertices) adjList = [] for i in range(order): tempRow = [] v = Graph.NearestVertex(graph, vertices[i]) adjVertices = Graph.AdjacentVertices(graph, v) for adjVertex in adjVertices: adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance) if not adjIndex == None: tempRow.append(adjIndex) tempRow.sort() adjList.append(tempRow) return adjList
def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, tolerance=0.0001)
-
Returns the adjacency matrix of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_matrix.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexKey
:str
, optional- If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None.
reverse
:bool
, optional- If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
edgeKeyFwd
:str
, optional- If set, the value at this key in the connecting edge from start vertex to end verrtex (forward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting.
edgeKeyBwd
:str
, optional- If set, the value at this key in the connecting edge from end vertex to start verrtex (backward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting.
bidirKey
:bool
, optional- If set to True or False, this key in the connecting edge will be used to determine is the edge is supposed to be bidirectional or not. If set to None, the input variable bidrectional will be used instead. The default is None
bidirectional
:bool
, optional- If set to True, the edges in the graph that do not have a bidireKey in their dictionaries will be treated as being bidirectional. Otherwise, the start vertex and end vertex of the connecting edge will determine the direction. The default is True.
useEdgeIndex
:bool , False
- If set to True, the adjacency matrix values will the index of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
useEdgeLength
:bool , False
- If set to True, the adjacency matrix values will the length of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The adjacency matrix.
Expand source code
@staticmethod def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, tolerance=0.0001): """ Returns the adjacency matrix of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_matrix. Parameters ---------- graph : topologic_core.Graph The input graph. vertexKey : str , optional If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None. reverse : bool , optional If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False. edgeKeyFwd : str , optional If set, the value at this key in the connecting edge from start vertex to end verrtex (forward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting. edgeKeyBwd : str , optional If set, the value at this key in the connecting edge from end vertex to start verrtex (backward) will be used instead of the value 1. The default is None. useEdgeIndex and useEdgeLength override this setting. bidirKey : bool , optional If set to True or False, this key in the connecting edge will be used to determine is the edge is supposed to be bidirectional or not. If set to None, the input variable bidrectional will be used instead. The default is None bidirectional : bool , optional If set to True, the edges in the graph that do not have a bidireKey in their dictionaries will be treated as being bidirectional. Otherwise, the start vertex and end vertex of the connecting edge will determine the direction. The default is True. useEdgeIndex : bool , False If set to True, the adjacency matrix values will the index of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used. useEdgeLength : bool , False If set to True, the adjacency matrix values will the length of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The adjacency matrix. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Helper import Helper if not Topology.IsInstance(graph, "Graph"): print("Graph.AdjacencyMatrix - Error: The input graph is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) if not vertexKey == None: sorting_values = [] for v in vertices: d = Topology.Dictionary(v) value = Dictionary.ValueAtKey(d, vertexKey) sorting_values.append(value) vertices = Helper.Sort(vertices, sorting_values) if reverse == True: vertices.reverse() edges = Graph.Edges(graph) order = len(vertices) matrix = [] # Initialize the matrix with zeroes for i in range(order): tempRow = [] for j in range(order): tempRow.append(0) matrix.append(tempRow) for i, edge in enumerate(edges): sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) svi = Vertex.Index(sv, vertices, tolerance=tolerance) evi = Vertex.Index(ev, vertices, tolerance=tolerance) if bidirKey == None: bidir = bidirectional else: bidir = Dictionary.ValueAtKey(Topology.Dictionary(edge), bidirKey) if bidir == None: bidir = bidirectional if edgeKeyFwd == None: valueFwd = 1 else: valueFwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyFwd) if valueFwd == None: valueFwd = 1 if edgeKeyBwd == None: valueBwd = 1 else: valueBwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyBwd) if valueBwd == None: valueBwd = 1 if useEdgeIndex: valueFwd = i+1 valueBwd = i+1 if useEdgeLength: valueFwd = Edge.Length(edge) valueBwd = Edge.Length(edge) matrix[svi][evi] = valueFwd if bidir: matrix[evi][svi] = valueBwd return matrix
def AdjacentVertices(graph, vertex)
-
Returns the list of vertices connected to the input vertex.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- the input vertex.
Returns
list
- The list of adjacent vertices.
Expand source code
@staticmethod def AdjacentVertices(graph, vertex): """ Returns the list of vertices connected to the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex the input vertex. Returns ------- list The list of adjacent vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AdjacentVertices - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.AdjacentVertices - Error: The input vertex is not a valid vertex. Returning None.") return None vertices = [] _ = graph.AdjacentVertices(vertex, vertices) return list(vertices)
def AllPaths(graph, vertexA, vertexB, timeLimit=10)
-
Returns all the paths that connect the input vertices within the allowed time limit in seconds.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
timeLimit
:int
, optional- The time limit in second. The default is 10 seconds.
Returns
list
- The list of all paths (wires) found within the time limit.
Expand source code
@staticmethod def AllPaths(graph, vertexA, vertexB, timeLimit=10): """ Returns all the paths that connect the input vertices within the allowed time limit in seconds. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. timeLimit : int , optional The time limit in second. The default is 10 seconds. Returns ------- list The list of all paths (wires) found within the time limit. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.AllPaths - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.AllPaths - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.AllPaths - Error: The input vertexB is not a valid vertex. Returning None.") return None paths = [] _ = graph.AllPaths(vertexA, vertexB, True, timeLimit, paths) return paths
def AverageClusteringCoefficient(graph, mantissa=6)
-
Returns the average clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient.
Parameters
graph
:topologic_core.Graph
- The input graph.
mantissa
:int
, optional- The desired length of the mantissa. The default is 6.
Returns
float
- The average clustering coefficient of the input graph.
Expand source code
@staticmethod def AverageClusteringCoefficient(graph, mantissa=6): """ Returns the average clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient. Parameters ---------- graph : topologic_core.Graph The input graph. mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- float The average clustering coefficient of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) if len(vertices) < 1: print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is a NULL graph. Returning None.") return None if len(vertices) == 1: return 0.0 lcc = Graph.LocalClusteringCoefficient(graph, vertices) acc = round(sum(lcc)/float(len(lcc)), mantissa) return acc
def BOTGraph(graph, bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel='Site_0001', siteDictionary=None, buildingLabel='Building_0001', buildingDictionary=None, storeyPrefix='Storey', floorLevels=[], labelKey='label', typeKey='type', geometryKey='brep', spaceType='space', wallType='wall', slabType='slab', doorType='door', windowType='window', contentType='content')
-
Creates an RDF graph according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/.
Parameters
graph
:topologic_core.Graph
- The input graph.
bidirectional
:bool
, optional- If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False.
includeAttributes
:bool
, optional- If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
includeLabel
:bool
, optional- If set to True, a label is attached to each node. Otherwise, it is not. The default is False.
includeGeometry
:bool
, optional- If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
siteLabel
:str
, optional- The desired site label. The default is "Site_0001".
siteDictionary
:dict
, optional- The dictionary of site attributes to include in the output. The default is None.
buildingLabel
:str
, optional- The desired building label. The default is "Building_0001".
buildingDictionary
:dict
, optional- The dictionary of building attributes to include in the output. The default is None.
storeyPrefix
:str
, optional- The desired prefixed to use for each building storey. The default is "Storey".
floorLevels
:list
, optional- The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute.
labelKey
:str
, optional- The dictionary key to use to look up the label of the node. The default is "label".
typeKey
:str
, optional- The dictionary key to use to look up the type of the node. The default is "type".
geometryKey
:str
, optional- The dictionary key to use to look up the geometry of the node. The default is "brep".
spaceType
:str
, optional- The dictionary string value to use to look up vertices of type "space". The default is "space".
wallType
:str
, optional- The dictionary string value to use to look up vertices of type "wall". The default is "wall".
slabType
:str
, optional- The dictionary string value to use to look up vertices of type "slab". The default is "slab".
doorType
:str
, optional- The dictionary string value to use to look up vertices of type "door". The default is "door".
windowType
:str
, optional- The dictionary string value to use to look up vertices of type "window". The default is "window".
contentType
:str
, optional- The dictionary string value to use to look up vertices of type "content". The default is "contents".
Returns
rdflib.graph.Graph
- The rdf graph using the BOT ontology.
Expand source code
@staticmethod def BOTGraph(graph, bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel = "Site_0001", siteDictionary = None, buildingLabel = "Building_0001", buildingDictionary = None , storeyPrefix = "Storey", floorLevels =[], labelKey="label", typeKey="type", geometryKey="brep", spaceType = "space", wallType = "wall", slabType = "slab", doorType = "door", windowType = "window", contentType = "content" ): """ Creates an RDF graph according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/. Parameters ---------- graph : topologic_core.Graph The input graph. bidirectional : bool , optional If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False. includeAttributes : bool , optional If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False. includeLabel : bool , optional If set to True, a label is attached to each node. Otherwise, it is not. The default is False. includeGeometry : bool , optional If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False. siteLabel : str , optional The desired site label. The default is "Site_0001". siteDictionary : dict , optional The dictionary of site attributes to include in the output. The default is None. buildingLabel : str , optional The desired building label. The default is "Building_0001". buildingDictionary : dict , optional The dictionary of building attributes to include in the output. The default is None. storeyPrefix : str , optional The desired prefixed to use for each building storey. The default is "Storey". floorLevels : list , optional The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute. labelKey : str , optional The dictionary key to use to look up the label of the node. The default is "label". typeKey : str , optional The dictionary key to use to look up the type of the node. The default is "type". geometryKey : str , optional The dictionary key to use to look up the geometry of the node. The default is "brep". spaceType : str , optional The dictionary string value to use to look up vertices of type "space". The default is "space". wallType : str , optional The dictionary string value to use to look up vertices of type "wall". The default is "wall". slabType : str , optional The dictionary string value to use to look up vertices of type "slab". The default is "slab". doorType : str , optional The dictionary string value to use to look up vertices of type "door". The default is "door". windowType : str , optional The dictionary string value to use to look up vertices of type "window". The default is "window". contentType : str , optional The dictionary string value to use to look up vertices of type "content". The default is "contents". Returns ------- rdflib.graph.Graph The rdf graph using the BOT ontology. """ from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import os import warnings try: from rdflib import Graph as RDFGraph from rdflib import URIRef, Literal, Namespace from rdflib.namespace import RDF, RDFS except: print("Graph.BOTGraph - Information: Installing required rdflib library.") try: os.system("pip install rdflib") except: os.system("pip install rdflib --user") try: from rdflib import Graph as RDFGraph from rdflib import URIRef, Literal, Namespace from rdflib.namespace import RDF, RDFS print("Graph.BOTGraph - Information: rdflib library installed correctly.") except: warnings.warn("Graph.BOTGraph - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.") return None if floorLevels == None: floorLevels = [] json_data = Graph.JSONData(graph, vertexLabelKey=labelKey) # Create an empty RDF graph bot_graph = RDFGraph() # Define namespaces rdf = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#") bot = Namespace("https://w3id.org/bot#") # Define a custom prefix mapping bot_graph.namespace_manager.bind("bot", bot) # Add site site_uri = URIRef(siteLabel) bot_graph.add((site_uri, rdf.type, bot.Site)) if includeLabel: bot_graph.add((site_uri, RDFS.label, Literal(siteLabel))) if Topology.IsInstance(siteDictionary, "Dictionary"): keys = Dictionary.Keys(siteDictionary) for key in keys: value = Dictionary.ValueAtKey(siteDictionary, key) if not (key == labelKey) and not (key == typeKey): bot_graph.add((site_uri, bot[key], Literal(value))) # Add building building_uri = URIRef(buildingLabel) bot_graph.add((building_uri, rdf.type, bot.Building)) if includeLabel: bot_graph.add((building_uri, RDFS.label, Literal(buildingLabel))) if Topology.IsInstance(buildingDictionary, "Dictionary"): keys = Dictionary.Keys(buildingDictionary) for key in keys: value = Dictionary.ValueAtKey(buildingDictionary, key) if key == labelKey: if includeLabel: bot_graph.add((building_uri, RDFS.label, Literal(value))) elif key != typeKey: bot_graph.add((building_uri, bot[key], Literal(value))) # Add stories # if floor levels are not given, then need to be computed if len(floorLevels) == 0: for node, attributes in json_data['vertices'].items(): if slabType.lower() in attributes[typeKey].lower(): floorLevels.append(attributes["z"]) floorLevels = list(set(floorLevels)) floorLevels.sort() floorLevels = floorLevels[:-1] storey_uris = [] n = max(len(str(len(floorLevels))),4) for i, floor_level in enumerate(floorLevels): storey_uri = URIRef(storeyPrefix+"_"+str(i+1).zfill(n)) bot_graph.add((storey_uri, rdf.type, bot.Storey)) if includeLabel: bot_graph.add((storey_uri, RDFS.label, Literal(storeyPrefix+"_"+str(i+1).zfill(n)))) storey_uris.append(storey_uri) # Add triples to relate building to site and stories to building bot_graph.add((site_uri, bot.hasBuilding, building_uri)) if bidirectional: bot_graph.add((building_uri, bot.isPartOf, site_uri)) # might not be needed for storey_uri in storey_uris: bot_graph.add((building_uri, bot.hasStorey, storey_uri)) if bidirectional: bot_graph.add((storey_uri, bot.isPartOf, building_uri)) # might not be needed # Add vertices as RDF resources for node, attributes in json_data['vertices'].items(): node_uri = URIRef(node) if spaceType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Space)) # Find the storey it is on z = attributes["z"] level = Helper.Position(z, floorLevels) if level > len(storey_uris): level = len(storey_uris) storey_uri = storey_uris[level-1] bot_graph.add((storey_uri, bot.hasSpace, node_uri)) if bidirectional: bot_graph.add((node_uri, bot.isPartOf, storey_uri)) # might not be needed elif windowType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Window)) elif doorType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Door)) elif wallType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Wall)) elif slabType.lower() in attributes[typeKey].lower(): bot_graph.add((node_uri, rdf.type, bot.Slab)) else: bot_graph.add((node_uri, rdf.type, bot.Element)) if includeAttributes: for key, value in attributes.items(): if key == geometryKey: if includeGeometry: bot_graph.add((node_uri, bot.hasSimpleGeometry, Literal(value))) if key == labelKey: if includeLabel: bot_graph.add((node_uri, RDFS.label, Literal(value))) elif key != typeKey and key != geometryKey: bot_graph.add((node_uri, bot[key], Literal(value))) if includeLabel: for key, value in attributes.items(): if key == labelKey: bot_graph.add((node_uri, RDFS.label, Literal(value))) # Add edges as RDF triples for edge, attributes in json_data['edges'].items(): source = attributes["source"] target = attributes["target"] source_uri = URIRef(source) target_uri = URIRef(target) if spaceType.lower() in json_data['vertices'][source][typeKey].lower() and spaceType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((source_uri, bot.adjacentTo, target_uri)) if bidirectional: bot_graph.add((target_uri, bot.adjacentTo, source_uri)) elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and wallType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((target_uri, bot.interfaceOf, source_uri)) elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and slabType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((target_uri, bot.interfaceOf, source_uri)) elif spaceType.lower() in json_data['vertices'][source][typeKey].lower() and contentType.lower() in json_data['vertices'][target][typeKey].lower(): bot_graph.add((source_uri, bot.containsElement, target_uri)) if bidirectional: bot_graph.add((target_uri, bot.isPartOf, source_uri)) else: bot_graph.add((source_uri, bot.connectsTo, target_uri)) if bidirectional: bot_graph.add((target_uri, bot.connectsTo, source_uri)) return bot_graph
def BOTString(graph, format='turtle', bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel='Site_0001', siteDictionary=None, buildingLabel='Building_0001', buildingDictionary=None, storeyPrefix='Storey', floorLevels=[], labelKey='label', typeKey='type', geometryKey='brep', spaceType='space', wallType='wall', slabType='slab', doorType='door', windowType='window', contentType='content')
-
Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/.
Parameters
graph
:topologic_core.Graph
- The input graph.
format
:str
, optional- The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads
bidirectional
:bool
, optional- If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False.
includeAttributes
:bool
, optional- If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
includeLabel
:bool
, optional- If set to True, a label is attached to each node. Otherwise, it is not. The default is False.
includeGeometry
:bool
, optional- If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
siteLabel
:str
, optional- The desired site label. The default is "Site_0001".
siteDictionary
:dict
, optional- The dictionary of site attributes to include in the output. The default is None.
buildingLabel
:str
, optional- The desired building label. The default is "Building_0001".
buildingDictionary
:dict
, optional- The dictionary of building attributes to include in the output. The default is None.
storeyPrefix
:str
, optional- The desired prefixed to use for each building storey. The default is "Storey".
floorLevels
:list
, optional- The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute.
typeKey
:str
, optional- The dictionary key to use to look up the type of the node. The default is "type".
labelKey
:str
, optional- The dictionary key to use to look up the label of the node. The default is "label".
spaceType
:str
, optional- The dictionary string value to use to look up vertices of type "space". The default is "space".
wallType
:str
, optional- The dictionary string value to use to look up vertices of type "wall". The default is "wall".
slabType
:str
, optional- The dictionary string value to use to look up vertices of type "slab". The default is "slab".
doorType
:str
, optional- The dictionary string value to use to look up vertices of type "door". The default is "door".
windowType
:str
, optional- The dictionary string value to use to look up vertices of type "window". The default is "window".
contentType
:str
, optional- The dictionary string value to use to look up vertices of type "content". The default is "contents".
Returns
str
- The rdf graph serialized string using the BOT ontology.
Expand source code
@staticmethod def BOTString(graph, format="turtle", bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel = "Site_0001", siteDictionary = None, buildingLabel = "Building_0001", buildingDictionary = None , storeyPrefix = "Storey", floorLevels =[], labelKey="label", typeKey="type", geometryKey="brep", spaceType = "space", wallType = "wall", slabType = "slab", doorType = "door", windowType = "window", contentType = "content", ): """ Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/. Parameters ---------- graph : topologic_core.Graph The input graph. format : str , optional The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads bidirectional : bool , optional If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False. includeAttributes : bool , optional If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False. includeLabel : bool , optional If set to True, a label is attached to each node. Otherwise, it is not. The default is False. includeGeometry : bool , optional If set to True, the geometry associated with vertices in the graph are written out. Otherwise, they are not. The default is False. siteLabel : str , optional The desired site label. The default is "Site_0001". siteDictionary : dict , optional The dictionary of site attributes to include in the output. The default is None. buildingLabel : str , optional The desired building label. The default is "Building_0001". buildingDictionary : dict , optional The dictionary of building attributes to include in the output. The default is None. storeyPrefix : str , optional The desired prefixed to use for each building storey. The default is "Storey". floorLevels : list , optional The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the vertices' 'z' attribute. typeKey : str , optional The dictionary key to use to look up the type of the node. The default is "type". labelKey : str , optional The dictionary key to use to look up the label of the node. The default is "label". spaceType : str , optional The dictionary string value to use to look up vertices of type "space". The default is "space". wallType : str , optional The dictionary string value to use to look up vertices of type "wall". The default is "wall". slabType : str , optional The dictionary string value to use to look up vertices of type "slab". The default is "slab". doorType : str , optional The dictionary string value to use to look up vertices of type "door". The default is "door". windowType : str , optional The dictionary string value to use to look up vertices of type "window". The default is "window". contentType : str , optional The dictionary string value to use to look up vertices of type "content". The default is "contents". Returns ------- str The rdf graph serialized string using the BOT ontology. """ bot_graph = Graph.BOTGraph(graph, bidirectional=bidirectional, includeAttributes=includeAttributes, includeLabel=includeLabel, includeGeometry=includeGeometry, siteLabel=siteLabel, siteDictionary=siteDictionary, buildingLabel=buildingLabel, buildingDictionary=buildingDictionary, storeyPrefix=storeyPrefix, floorLevels=floorLevels, labelKey=labelKey, typeKey=typeKey, geometryKey=geometryKey, spaceType = spaceType, wallType = wallType, slabType = slabType, doorType = doorType, windowType = windowType, contentType = contentType ) return bot_graph.serialize(format=format)
def BetweenessCentrality(graph, vertices=None, sources=None, destinations=None, tolerance=0.001)
-
Returns the betweeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the betweeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Betweenness_centrality.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertices
:list
, optional- The input list of vertices. The default is None which means all vertices in the input graph are considered.
sources
:list
, optional- The input list of source vertices. The default is None which means all vertices in the input graph are considered.
destinations
:list
, optional- The input list of destination vertices. The default is None which means all vertices in the input graph are considered.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The betweeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
Expand source code
@staticmethod def BetweenessCentrality(graph, vertices=None, sources=None, destinations=None, tolerance=0.001): """ Returns the betweeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the betweeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Betweenness_centrality. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. The default is None which means all vertices in the input graph are considered. sources : list , optional The input list of source vertices. The default is None which means all vertices in the input graph are considered. destinations : list , optional The input list of destination vertices. The default is None which means all vertices in the input graph are considered. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The betweeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology def betweeness(vertices, topologies, tolerance=0.001): returnList = [0] * len(vertices) for topology in topologies: t_vertices = Topology.Vertices(topology) for t_v in t_vertices: index = Vertex.Index(t_v, vertices, strict=False, tolerance=tolerance) if not index == None: returnList[index] = returnList[index]+1 return returnList if not Topology.IsInstance(graph, "Graph"): print("Graph.BetweenessCentrality - Error: The input graph is not a valid graph. Returning None.") return None graphVertices = Graph.Vertices(graph) if not isinstance(vertices, list): vertices = graphVertices else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.BetweenessCentrality - Error: The input list of vertices does not contain valid vertices. Returning None.") return None if not isinstance(sources, list): sources = graphVertices else: sources = [v for v in sources if Topology.IsInstance(v, "Vertex")] if len(sources) < 1: print("Graph.BetweenessCentrality - Error: The input list of sources does not contain valid vertices. Returning None.") return None if not isinstance(destinations, list): destinations = graphVertices else: destinations = [v for v in destinations if Topology.IsInstance(v, "Vertex")] if len(destinations) < 1: print("Graph.BetweenessCentrality - Error: The input list of destinations does not contain valid vertices. Returning None.") return None paths = [] try: for so in tqdm(sources, desc="Computing Shortest Paths", leave=False): v1 = Graph.NearestVertex(graph, so) for si in destinations: v2 = Graph.NearestVertex(graph, si) if not v1 == v2: path = Graph.ShortestPath(graph, v1, v2) if path: paths.append(path) except: for so in sources: v1 = Graph.NearestVertex(graph, so) for si in destinations: v2 = Graph.NearestVertex(graph, si) if not v1 == v2: path = Graph.ShortestPath(graph, v1, v2) if path: paths.append(path) values = betweeness(vertices, paths, tolerance=tolerance) minValue = min(values) maxValue = max(values) size = maxValue - minValue values = [(v-minValue)/size for v in values] return values
def ByAdjacencyMatrix(adjacencyMatrix, xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5)
-
Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema.
Parameters
adjacencyMatrix
:list
- The adjacency matrix expressed as a nested list of 0s and 1s.
xMin
:float
, optional- The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
yMin
:float
, optional- The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
zMin
:float
, optional- The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
xMax
:float
, optional- The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
yMax
:float
, optional- The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
zMax
:float
, optional- The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
Returns
topologic_core.Graph
- The created graph.
Expand source code
@staticmethod def ByAdjacencyMatrix(adjacencyMatrix, xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5): """ Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema. Parameters ---------- adjacencyMatrix : list The adjacency matrix expressed as a nested list of 0s and 1s. xMin : float, optional The desired minimum value to assign for a vertex's X coordinate. The default is -0.5. yMin : float, optional The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5. zMin : float, optional The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5. xMax : float, optional The desired maximum value to assign for a vertex's X coordinate. The default is 0.5. yMax : float, optional The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5. zMax : float, optional The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge import random if not isinstance(adjacencyMatrix, list): print("Graph.ByAdjacencyMatrix - Error: The input adjacencyMatrix parameter is not a valid list. Returning None.") return None # Add vertices with random coordinates vertices = [] for i in range(len(adjacencyMatrix)): x, y, z = random.uniform(xMin,xMax), random.uniform(yMin,yMax), random.uniform(zMin,zMax) vertices.append(Vertex.ByCoordinates(x, y, z)) # Create the graph using vertices and edges if len(vertices) == 0: print("Graph.ByAdjacencyMatrix - Error: The graph does not contain any vertices. Returning None.") return None # Add edges based on the adjacency matrix edges = [] for i in range(len(adjacencyMatrix)): for j in range(i+1, len(adjacencyMatrix)): if not adjacencyMatrix[i][j] == 0: edges.append(Edge.ByVertices([vertices[i], vertices[j]])) return Graph.ByVerticesEdges(vertices, edges)
def ByAdjacencyMatrixCSVPath(path)
-
Returns graphs according to the input path. This method assumes the CSV files follow an adjacency matrix schema.
Parameters
path
:str
- The file path to the adjacency matrix CSV file.
Returns
topologic_core.Graph
- The created graph.
Expand source code
@staticmethod def ByAdjacencyMatrixCSVPath(path): """ Returns graphs according to the input path. This method assumes the CSV files follow an adjacency matrix schema. Parameters ---------- path : str The file path to the adjacency matrix CSV file. Returns ------- topologic_core.Graph The created graph. """ # Read the adjacency matrix from CSV file using pandas adjacency_matrix_df = pd.read_csv(path, header=None) # Convert DataFrame to a nested list adjacency_matrix = adjacency_matrix_df.values.tolist() return Graph.ByAdjacencyMatrix(adjacencyMatrix=adjacency_matrix)
def ByBOTGraph(botGraph, includeContext=False, xMin=-0.5, xMax=0.5, yMin=-0.5, yMax=0.5, zMin=-0.5, zMax=0.5, tolerance=0.0001)
-
Expand source code
@staticmethod def ByBOTGraph(botGraph, includeContext = False, xMin = -0.5, xMax = 0.5, yMin = -0.5, yMax = 0.5, zMin = -0.5, zMax = 0.5, tolerance = 0.0001 ): def value_by_string(s): if s.lower() == "true": return True if s.lower() == "false": return False vt = "str" s2 = s.strip("-") if s2.isnumeric(): vt = "int" else: try: s3 = s2.split(".")[0] s4 = s2.split(".")[1] if (s3.isnumeric() or s4.isnumeric()): vt = "float" except: vt = "str" if vt == "str": return s elif vt == "int": return int(s) elif vt == "float": return float(s) def collect_nodes_by_type(rdf_graph, node_type=None): results = set() if node_type is not None: for subj, pred, obj in rdf_graph.triples((None, None, None)): if "type" in pred.lower(): if node_type.lower() in obj.lower(): results.add(subj) return list(results) def collect_attributes_for_subject(rdf_graph, subject): attributes = {} for subj, pred, obj in rdf_graph.triples((subject, None, None)): predicate_str = str(pred) object_str = str(obj) attributes[predicate_str] = object_str return attributes def get_triples_by_predicate_type(rdf_graph, predicate_type): triples = [] for subj, pred, obj in rdf_graph: if pred.split('#')[-1].lower() == predicate_type.lower(): triples.append((str(subj), str(pred), str(obj))) return triples from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Graph import Graph from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import random try: import rdflib except: print("Graph.BOTGraph - Information: Installing required rdflib library.") try: os.system("pip install rdflib") except: os.system("pip install rdflib --user") try: import rdflib print("Graph.BOTGraph - Information: rdflib library installed correctly.") except: warnings.warn("Graph.BOTGraph - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.") return None predicates = ['adjacentto', 'interfaceof', 'containselement', 'connectsto'] bot_types = ['Space', 'Wall', 'Slab', 'Door', 'Window', 'Element'] if includeContext: predicates += ['hasspace', 'hasbuilding', 'hasstorey'] bot_types += ['Site', 'Building', 'Storey'] namespaces = botGraph.namespaces() for ns in namespaces: if 'bot' in ns[0].lower(): bot_namespace = ns break ref = bot_namespace[1] nodes = [] for bot_type in bot_types: node_type = rdflib.term.URIRef(ref+bot_type) nodes +=collect_nodes_by_type(botGraph, node_type=node_type) vertices = [] dic = {} for node in nodes: x, y, z = random.uniform(xMin,xMax), random.uniform(yMin,yMax), random.uniform(zMin,zMax) d_keys = ["bot_id"] d_values = [str(node)] attributes = collect_attributes_for_subject(botGraph, node) keys = attributes.keys() for key in keys: key_type = key.split('#')[-1] if key_type.lower() not in predicates: if 'x' == key_type.lower(): x = value_by_string(attributes[key]) d_keys.append('x') d_values.append(x) elif 'y' == key_type.lower(): y = value_by_string(attributes[key]) d_keys.append('y') d_values.append(y) elif 'z' == key_type.lower(): z = value_by_string(attributes[key]) d_keys.append('z') d_values.append(z) else: d_keys.append(key_type.lower()) d_values.append(value_by_string(attributes[key].split("#")[-1])) d = Dictionary.ByKeysValues(d_keys, d_values) v = Vertex.ByCoordinates(x,y,z) v = Topology.SetDictionary(v, d) dic[str(node)] = v vertices.append(v) edges = [] for predicate in predicates: triples = get_triples_by_predicate_type(botGraph, predicate) for triple in triples: subj = triple[0] obj = triple[2] sv = dic[subj] ev = dic[obj] e = Edge.ByVertices([sv,ev], tolerance=tolerance) d = Dictionary.ByKeyValue("type", predicate) e = Topology.SetDictionary(e, d) edges.append(e) return Graph.ByVerticesEdges(vertices, edges)
def ByBOTPath(path, includeContext=False, xMin=-0.5, xMax=0.5, yMin=-0.5, yMax=0.5, zMin=-0.5, zMax=0.5, tolerance=0.0001)
-
Expand source code
@staticmethod def ByBOTPath(path, includeContext = False, xMin = -0.5, xMax = 0.5, yMin = -0.5, yMax = 0.5, zMin = -0.5, zMax = 0.5, tolerance = 0.0001 ): try: from rdflib import Graph as RDFGraph except: print("Graph.ByBOTPath - Information: Installing required rdflib library.") try: os.system("pip install rdflib") except: os.system("pip install rdflib --user") try: from rdflib import Graph as RDFGraph print("Graph.ByBOTPath - Information: rdflib library installed correctly.") except: warnings.warn("Graph.ByBOTPath - Error: Could not import rdflib. Please try to install rdflib manually. Returning None.") return None bot_graph = RDFGraph() bot_graph.parse(path) return Graph.ByBOTGraph(bot_graph, includeContext = includeContext, xMin = xMin, xMax = xMax, yMin = yMin, yMax = yMax, zMin = zMin, zMax = zMax, tolerance = tolerance )
def ByCSVPath(path, graphIDHeader='graph_id', graphLabelHeader='label', graphFeaturesHeader='feat', graphFeaturesKeys=[], edgeSRCHeader='src_id', edgeDSTHeader='dst_id', edgeLabelHeader='label', edgeTrainMaskHeader='train_mask', edgeValidateMaskHeader='val_mask', edgeTestMaskHeader='test_mask', edgeFeaturesHeader='feat', edgeFeaturesKeys=[], nodeIDHeader='node_id', nodeLabelHeader='label', nodeTrainMaskHeader='train_mask', nodeValidateMaskHeader='val_mask', nodeTestMaskHeader='test_mask', nodeFeaturesHeader='feat', nodeXHeader='X', nodeYHeader='Y', nodeZHeader='Z', nodeFeaturesKeys=[], tolerance=0.0001)
-
Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema.
Parameters
path
:str
- The path to the folder containing the .yaml and .csv files for graphs, edges, and nodes.
graphIDHeader
:str
, optional- The column header string used to specify the graph id. The default is "graph_id".
graphLabelHeader
:str
, optional- The column header string used to specify the graph label. The default is "label".
graphFeaturesHeader
:str
, optional- The column header string used to specify the graph features. The default is "feat".
edgeSRCHeader
:str
, optional- The column header string used to specify the source vertex id of edges. The default is "src_id".
edgeDSTHeader
:str
, optional- The column header string used to specify the destination vertex id of edges. The default is "dst_id".
edgeLabelHeader
:str
, optional- The column header string used to specify the label of edges. The default is "label".
edgeTrainMaskHeader
:str
, optional- The column header string used to specify the train mask of edges. The default is "train_mask".
edgeValidateMaskHeader
:str
, optional- The column header string used to specify the validate mask of edges. The default is "val_mask".
edgeTestMaskHeader
:str
, optional- The column header string used to specify the test mask of edges. The default is "test_mask".
edgeFeaturesHeader
:str
, optional- The column header string used to specify the features of edges. The default is "feat".
edgeFeaturesKeys
:list
, optional- The list of dicitonary keys to use to index the edge features. The length of this list must match the length of edge features. The default is [].
nodeIDHeader
:str
, optional- The column header string used to specify the id of nodes. The default is "node_id".
nodeLabelHeader
:str
, optional- The column header string used to specify the label of nodes. The default is "label".
nodeTrainMaskHeader
:str
, optional- The column header string used to specify the train mask of nodes. The default is "train_mask".
nodeValidateMaskHeader
:str
, optional- The column header string used to specify the validate mask of nodes. The default is "val_mask".
nodeTestMaskHeader
:str
, optional- The column header string used to specify the test mask of nodes. The default is "test_mask".
nodeFeaturesHeader
:str
, optional- The column header string used to specify the features of nodes. The default is "feat".
nodeXHeader
:str
, optional- The column header string used to specify the X coordinate of nodes. The default is "X".
nodeYHeader
:str
, optional- The column header string used to specify the Y coordinate of nodes. The default is "Y".
nodeZHeader
:str
, optional- The column header string used to specify the Z coordinate of nodes. The default is "Z".
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
dict
- The dictionary of DGL graphs and labels found in the input CSV files. The keys in the dictionary are "graphs", "labels", "features"
Expand source code
@staticmethod def ByCSVPath(path, graphIDHeader="graph_id", graphLabelHeader="label", graphFeaturesHeader="feat", graphFeaturesKeys=[], edgeSRCHeader="src_id", edgeDSTHeader="dst_id", edgeLabelHeader="label", edgeTrainMaskHeader="train_mask", edgeValidateMaskHeader="val_mask", edgeTestMaskHeader="test_mask", edgeFeaturesHeader="feat", edgeFeaturesKeys=[], nodeIDHeader="node_id", nodeLabelHeader="label", nodeTrainMaskHeader="train_mask", nodeValidateMaskHeader="val_mask", nodeTestMaskHeader="test_mask", nodeFeaturesHeader="feat", nodeXHeader="X", nodeYHeader="Y", nodeZHeader="Z", nodeFeaturesKeys=[], tolerance=0.0001): """ Returns graphs according to the input folder path. This method assumes the CSV files follow DGL's schema. Parameters ---------- path : str The path to the folder containing the .yaml and .csv files for graphs, edges, and nodes. graphIDHeader : str , optional The column header string used to specify the graph id. The default is "graph_id". graphLabelHeader : str , optional The column header string used to specify the graph label. The default is "label". graphFeaturesHeader : str , optional The column header string used to specify the graph features. The default is "feat". edgeSRCHeader : str , optional The column header string used to specify the source vertex id of edges. The default is "src_id". edgeDSTHeader : str , optional The column header string used to specify the destination vertex id of edges. The default is "dst_id". edgeLabelHeader : str , optional The column header string used to specify the label of edges. The default is "label". edgeTrainMaskHeader : str , optional The column header string used to specify the train mask of edges. The default is "train_mask". edgeValidateMaskHeader : str , optional The column header string used to specify the validate mask of edges. The default is "val_mask". edgeTestMaskHeader : str , optional The column header string used to specify the test mask of edges. The default is "test_mask". edgeFeaturesHeader : str , optional The column header string used to specify the features of edges. The default is "feat". edgeFeaturesKeys : list , optional The list of dicitonary keys to use to index the edge features. The length of this list must match the length of edge features. The default is []. nodeIDHeader : str , optional The column header string used to specify the id of nodes. The default is "node_id". nodeLabelHeader : str , optional The column header string used to specify the label of nodes. The default is "label". nodeTrainMaskHeader : str , optional The column header string used to specify the train mask of nodes. The default is "train_mask". nodeValidateMaskHeader : str , optional The column header string used to specify the validate mask of nodes. The default is "val_mask". nodeTestMaskHeader : str , optional The column header string used to specify the test mask of nodes. The default is "test_mask". nodeFeaturesHeader : str , optional The column header string used to specify the features of nodes. The default is "feat". nodeXHeader : str , optional The column header string used to specify the X coordinate of nodes. The default is "X". nodeYHeader : str , optional The column header string used to specify the Y coordinate of nodes. The default is "Y". nodeZHeader : str , optional The column header string used to specify the Z coordinate of nodes. The default is "Z". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- dict The dictionary of DGL graphs and labels found in the input CSV files. The keys in the dictionary are "graphs", "labels", "features" """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary import os from os.path import exists, isdir import yaml import glob import random import numbers def find_yaml_files(folder_path): yaml_files = glob.glob(f"{folder_path}/*.yaml") return yaml_files def read_yaml(file_path): with open(file_path, 'r') as file: data = yaml.safe_load(file) edge_data = data.get('edge_data', []) node_data = data.get('node_data', []) graph_data = data.get('graph_data', {}) edges_path = edge_data[0].get('file_name') if edge_data else None nodes_path = node_data[0].get('file_name') if node_data else None graphs_path = graph_data.get('file_name') return graphs_path, edges_path, nodes_path if not exists(path): print("Graph.ByCSVPath - Error: the input path parameter does not exists. Returning None.") return None if not isdir(path): print("Graph.ByCSVPath - Error: the input path parameter is not a folder. Returning None.") return None yaml_files = find_yaml_files(path) if len(yaml_files) < 1: print("Graph.ByCSVPath - Error: the input path parameter does not contain any valid YAML files. Returning None.") return None yaml_file = yaml_files[0] yaml_file_path = os.path.join(path, yaml_file) graphs_path, edges_path, nodes_path = read_yaml(yaml_file_path) if not graphs_path == None: graphs_path = os.path.join(path, graphs_path) if graphs_path == None: print("Graph.ByCSVPath - Warning: a graphs.csv file does not exist inside the folder specified by the input path parameter. Will assume the dataset includes only one graph.") graphs_df = pd.DataFrame() graph_ids=[0] graph_labels=[0] graph_features=[None] else: graphs_df = pd.read_csv(graphs_path) graph_ids = [] graph_labels = [] graph_features = [] if not edges_path == None: edges_path = os.path.join(path, edges_path) if not exists(edges_path): print("Graph.ByCSVPath - Error: an edges.csv file does not exist inside the folder specified by the input path parameter. Returning None.") return None edges_path = os.path.join(path, edges_path) edges_df = pd.read_csv(edges_path) grouped_edges = edges_df.groupby(graphIDHeader) if not nodes_path == None: nodes_path = os.path.join(path, nodes_path) if not exists(nodes_path): print("Graph.ByCSVPath - Error: a nodes.csv file does not exist inside the folder specified by the input path parameter. Returning None.") return None nodes_df = pd.read_csv(nodes_path) # Group nodes and nodes by their 'graph_id' grouped_nodes = nodes_df.groupby(graphIDHeader) if len(nodeFeaturesKeys) == 0: node_keys = [nodeIDHeader, nodeLabelHeader, "mask", nodeFeaturesHeader] else: node_keys = [nodeIDHeader, nodeLabelHeader, "mask"]+nodeFeaturesKeys if len(edgeFeaturesKeys) == 0: edge_keys = [edgeLabelHeader, "mask", edgeFeaturesHeader] else: edge_keys = [edgeLabelHeader, "mask"]+edgeFeaturesKeys if len(graphFeaturesKeys) == 0: graph_keys = [graphIDHeader, graphLabelHeader, graphFeaturesHeader] else: graph_keys = [graphIDHeader, graphLabelHeader]+graphFeaturesKeys # Iterate through the graphs DataFrame for index, row in graphs_df.iterrows(): graph_ids.append(row[graphIDHeader]) graph_labels.append(row[graphLabelHeader]) graph_features.append(row[graphFeaturesHeader]) vertices_ds = [] # A list to hold the vertices data structures until we can build the actual graphs # Iterate through the grouped nodes DataFrames for graph_id, group_node_df in grouped_nodes: vertices = [] verts = [] #This is a list of x, y, z tuples to make sure the vertices have unique locations. n_verts = 0 for index, row in group_node_df.iterrows(): n_verts += 1 node_id = row[nodeIDHeader] label = row[nodeLabelHeader] train_mask = row[nodeTrainMaskHeader] val_mask = row[nodeValidateMaskHeader] test_mask = row[nodeTestMaskHeader] mask = 0 if [train_mask, val_mask, test_mask] == [True, False, False]: mask = 0 elif [train_mask, val_mask, test_mask] == [False, True, False]: mask = 1 elif [train_mask, val_mask, test_mask] == [False, False, True]: mask = 2 else: mask = 0 features = row[nodeFeaturesHeader] x = row[nodeXHeader] y = row[nodeYHeader] z = row[nodeZHeader] if not isinstance(x, numbers.Number): x = random.randrange(0,1000) if not isinstance(y, numbers.Number): y = random.randrange(0,1000) if not isinstance(z, numbers.Number): z = random.randrange(0,1000) while [x, y, z] in verts: x = x + random.randrange(10000,30000,1000) y = y + random.randrange(4000,6000, 100) z = z + random.randrange(70000,90000, 1000) verts.append([x, y, z]) v = Vertex.ByCoordinates(x, y, z) if Topology.IsInstance(v, "Vertex"): if len(nodeFeaturesKeys) == 0: values = [node_id, label, mask, features] else: values = [node_id, label, mask] featureList = features.split(",") featureList = [float(s) for s in featureList] values = [node_id, label, mask]+featureList d = Dictionary.ByKeysValues(node_keys, values) if Topology.IsInstance(d, "Dictionary"): v = Topology.SetDictionary(v, d) else: print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created vertex.") vertices.append(v) else: print("Graph.ByCSVPath - Warning: Failed to create and add a vertex to the list of vertices.") vertices_ds.append(vertices) edges_ds = [] # A list to hold the vertices data structures until we can build the actual graphs # Access specific columns within the grouped DataFrame for graph_id, group_edge_df in grouped_edges: #vertices = vertices_ds[graph_id] edges = [] es = [] # a list to check for duplicate edges duplicate_edges = 0 for index, row in group_edge_df.iterrows(): src_id = int(row[edgeSRCHeader]) dst_id = int(row[edgeDSTHeader]) label = row[nodeLabelHeader] train_mask = row[edgeTrainMaskHeader] val_mask = row[edgeValidateMaskHeader] test_mask = row[edgeTestMaskHeader] mask = 0 if [train_mask, val_mask, test_mask] == [True, False, False]: mask = 0 elif [train_mask, val_mask, test_mask] == [False, True, False]: mask = 1 elif [train_mask, val_mask, test_mask] == [False, False, True]: mask = 2 else: mask = 0 features = row[edgeFeaturesHeader] if len(edgeFeaturesKeys) == 0: values = [label, mask, features] else: featureList = features.split(",") featureList = [float(s) for s in featureList] values = [label, mask]+featureList if not (src_id == dst_id) and not [src_id, dst_id] in es and not [dst_id, src_id] in es: es.append([src_id, dst_id]) edge = Edge.ByVertices([vertices[src_id], vertices[dst_id]], tolerance=tolerance) if Topology.IsInstance(edge, "Edge"): d = Dictionary.ByKeysValues(edge_keys, values) if Topology.IsInstance(d, "Dictionary"): edge = Topology.SetDictionary(edge, d) else: print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created edge.") edges.append(edge) else: print("Graph.ByCSVPath - Warning: Failed to create and add an edge to the list of edges.") else: duplicate_edges += 1 if duplicate_edges > 0: print("Graph.ByCSVPath - Warning: Found", duplicate_edges, "duplicate edges in graph id:", graph_id) edges_ds.append(edges) # Build the graphs graphs = [] for i, vertices, in enumerate(vertices_ds): edges = edges_ds[i] g = Graph.ByVerticesEdges(vertices, edges) temp_v = Graph.Vertices(g) temp_e = Graph.Edges(g) if Topology.IsInstance(g, "Graph"): if len(graphFeaturesKeys) == 0: values = [graph_ids[i], graph_labels[i], graph_features[i]] else: values = [graph_ids[i], graph_labels[i]] featureList = graph_features[i].split(",") featureList = [float(s) for s in featureList] values = [graph_ids[i], graph_labels[i]]+featureList l1 = len(graph_keys) l2 = len(values) if not l1 == l2: print("Graph.ByCSVPath - Error: The length of the keys and values lists do not match. Returning None.") return None d = Dictionary.ByKeysValues(graph_keys, values) if Topology.IsInstance(d, "Dictionary"): g = Graph.SetDictionary(g, d) else: print("Graph.ByCSVPath - Warning: Failed to create and add a dictionary to the created graph.") graphs.append(g) else: print("Graph.ByCSVPath - Error: Failed to create and add a graph to the list of graphs.") return {"graphs": graphs, "labels": graph_labels, "features": graph_features}
def ByDGCNNFile(file, key: str = 'label', tolerance: float = 0.0001)
-
Creates a graph from a DGCNN File.
Parameters
file
:file object
- The input file.
key
:str
, optional- The desired key for storing the node label. The default is "label".
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
dict
- A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'.
Expand source code
@staticmethod def ByDGCNNFile(file, key: str = "label", tolerance: float = 0.0001): """ Creates a graph from a DGCNN File. Parameters ---------- file : file object The input file. key : str , optional The desired key for storing the node label. The default is "label". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- dict A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'. """ if not file: print("Graph.ByDGCNNFile - Error: The input file is not a valid file. Returning None.") return None dgcnn_string = file.read() file.close() return Graph.ByDGCNNString(dgcnn_string, key=key, tolerance=tolerance)
def ByDGCNNPath(path, key: str = 'label', tolerance: float = 0.0001)
-
Creates a graph from a DGCNN path.
Parameters
path
:str
- The input file path.
key
:str
, optional- The desired key for storing the node label. The default is "label".
tolerance
:str
, optional- The desired tolerance. The default is 0.0001.
Returns
dict
- A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'.
Expand source code
@staticmethod def ByDGCNNPath(path, key: str = "label", tolerance: float = 0.0001): """ Creates a graph from a DGCNN path. Parameters ---------- path : str The input file path. key : str , optional The desired key for storing the node label. The default is "label". tolerance : str , optional The desired tolerance. The default is 0.0001. Returns ------- dict A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'. """ if not path: print("Graph.ByDGCNNPath - Error: the input path is not a valid path. Returning None.") return None try: file = open(path) except: print("Graph.ByDGCNNPath - Error: the DGCNN file is not a valid file. Returning None.") return None return Graph.ByDGCNNFile(file, key=key, tolerance=tolerance)
def ByDGCNNString(string, key: str = 'label', tolerance: float = 0.0001)
-
Creates a graph from a DGCNN string.
Parameters
string
:str
- The input string.
key
:str
, optional- The desired key for storing the node label. The default is "label".
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
dict
- A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'.
Expand source code
@staticmethod def ByDGCNNString(string, key: str ="label", tolerance: float = 0.0001): """ Creates a graph from a DGCNN string. Parameters ---------- string : str The input string. key : str , optional The desired key for storing the node label. The default is "label". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- dict A dictionary with the graphs and labels. The keys are 'graphs' and 'labels'. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary import random def verticesByCoordinates(x_coords, y_coords): vertices = [] for i in range(len(x_coords)): vertices.append(Vertex.ByCoordinates(x_coords[i], y_coords[i], 0)) return vertices graphs = [] labels = [] lines = string.split("\n") n_graphs = int(lines[0]) index = 1 for i in range(n_graphs): edges = [] line = lines[index].split() n_nodes = int(line[0]) graph_label = int(line[1]) labels.append(graph_label) index+=1 x_coordinates = random.sample(range(0, n_nodes), n_nodes) y_coordinates = random.sample(range(0, n_nodes), n_nodes) vertices = verticesByCoordinates(x_coordinates, y_coordinates) for j in range(n_nodes): line = lines[index+j].split() node_label = int(line[0]) node_dict = Dictionary.ByKeysValues([key], [node_label]) Topology.SetDictionary(vertices[j], node_dict) for j in range(n_nodes): line = lines[index+j].split() sv = vertices[j] adj_vertices = line[2:] for adj_vertex in adj_vertices: ev = vertices[int(adj_vertex)] e = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance) edges.append(e) index+=n_nodes graphs.append(Graph.ByVerticesEdges(vertices, edges)) return {'graphs':graphs, 'labels':labels}
def ByIFCFile(file, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5)
-
Create a Graph from an IFC file. This code is partially based on code from Bruno Postle.
Parameters
file
:file
- The input IFC file
includeTypes
:list
, optional- A list of IFC object types to include in the graph. The default is [] which means all object types are included.
excludeTypes
:list
, optional- A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded.
includeRels
:list
, optional- A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included.
excludeRels
:list
, optional- A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded.
xMin
:float
, optional- The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
yMin
:float
, optional- The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
zMin
:float
, optional- The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
xMax
:float
, optional- The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
yMax
:float
, optional- The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
zMax
:float
, optional- The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
Returns
topologic_core.Graph
- The created graph.
Expand source code
@staticmethod def ByIFCFile(file, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5): """ Create a Graph from an IFC file. This code is partially based on code from Bruno Postle. Parameters ---------- file : file The input IFC file includeTypes : list , optional A list of IFC object types to include in the graph. The default is [] which means all object types are included. excludeTypes : list , optional A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded. includeRels : list , optional A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included. excludeRels : list , optional A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded. xMin : float, optional The desired minimum value to assign for a vertex's X coordinate. The default is -0.5. yMin : float, optional The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5. zMin : float, optional The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5. xMax : float, optional The desired maximum value to assign for a vertex's X coordinate. The default is 0.5. yMax : float, optional The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5. zMax : float, optional The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Topology import Topology from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Graph import Graph from topologicpy.Dictionary import Dictionary try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom except: print("Graph.ByIFCFile - Warning: Installing required ifcopenshell library.") try: os.system("pip install ifcopenshell") except: os.system("pip install ifcopenshell --user") try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom print("Graph.ByIFCFile - Warning: ifcopenshell library installed correctly.") except: warnings.warn("Graph.ByIFCFile - Error: Could not import ifcopenshell. Please try to install ifcopenshell manually. Returning None.") return None import random def vertexAtKeyValue(vertices, key, value): for v in vertices: d = Topology.Dictionary(v) d_value = Dictionary.ValueAtKey(d, key) if value == d_value: return v return None def IFCObjects(ifc_file, include=[], exclude=[]): include = [s.lower() for s in include] exclude = [s.lower() for s in exclude] all_objects = ifc_file.by_type('IfcProduct') return_objects = [] for obj in all_objects: is_a = obj.is_a().lower() if is_a in exclude: continue if is_a in include or len(include) == 0: return_objects.append(obj) return return_objects def IFCObjectTypes(ifc_file): products = IFCObjects(ifc_file) obj_types = [] for product in products: obj_types.append(product.is_a()) obj_types = list(set(obj_types)) obj_types.sort() return obj_types def IFCRelationshipTypes(ifc_file): rel_types = [ifc_rel.is_a() for ifc_rel in ifc_file.by_type("IfcRelationship")] rel_types = list(set(rel_types)) rel_types.sort() return rel_types def IFCRelationships(ifc_file, include=[], exclude=[]): include = [s.lower() for s in include] exclude = [s.lower() for s in exclude] rel_types = [ifc_rel.is_a() for ifc_rel in ifc_file.by_type("IfcRelationship")] rel_types = list(set(rel_types)) relationships = [] for ifc_rel in ifc_file.by_type("IfcRelationship"): rel_type = ifc_rel.is_a().lower() if rel_type in exclude: continue if rel_type in include or len(include) == 0: relationships.append(ifc_rel) return relationships def vertexByIFCObject(ifc_object, object_types, restrict=False): settings = ifcopenshell.geom.settings() settings.set(settings.USE_BREP_DATA,False) settings.set(settings.SEW_SHELLS,True) settings.set(settings.USE_WORLD_COORDS,True) try: shape = ifcopenshell.geom.create_shape(settings, ifc_object) except: shape = None if shape or restrict == False: #Only add vertices of entities that have 3D geometries. obj_id = ifc_object.id() psets = ifcopenshell.util.element.get_psets(ifc_object) obj_type = ifc_object.is_a() obj_type_id = object_types.index(obj_type) name = "Untitled" LongName = "Untitled" try: name = ifc_object.Name except: name = "Untitled" try: LongName = ifc_object.LongName except: LongName = name if name == None: name = "Untitled" if LongName == None: LongName = "Untitled" label = str(obj_id)+" "+LongName+" ("+obj_type+" "+str(obj_type_id)+")" try: grouped_verts = ifcopenshell.util.shape.get_vertices(shape.geometry) vertices = [Vertex.ByCoordinates(list(coords)) for coords in grouped_verts] centroid = Vertex.Centroid(vertices) except: x = random.uniform(xMin,xMax) y = random.uniform(yMin,yMax) z = random.uniform(zMin,zMax) centroid = Vertex.ByCoordinates(x, y, z) d = Dictionary.ByKeysValues(["id","psets", "type", "type_id", "name", "label"], [obj_id, psets, obj_type, obj_type_id, name, label]) centroid = Topology.SetDictionary(centroid, d) return centroid return None def edgesByIFCRelationships(ifc_relationships, ifc_types, vertices): tuples = [] edges = [] for ifc_rel in ifc_relationships: source = None destinations = [] if ifc_rel.is_a("IfcRelAggregates"): source = ifc_rel.RelatingObject destinations = ifc_rel.RelatedObjects if ifc_rel.is_a("IfcRelNests"): source = ifc_rel.RelatingObject destinations = ifc_rel.RelatedObjects if ifc_rel.is_a("IfcRelAssignsToGroup"): source = ifc_rel.RelatingGroup destinations = ifc_rel.RelatedObjects if ifc_rel.is_a("IfcRelConnectsPathElements"): source = ifc_rel.RelatingElement destinations = [ifc_rel.RelatedElement] if ifc_rel.is_a("IfcRelConnectsStructuralMember"): source = ifc_rel.RelatingStructuralMember destinations = [ifc_rel.RelatedStructuralConnection] if ifc_rel.is_a("IfcRelContainedInSpatialStructure"): source = ifc_rel.RelatingStructure destinations = ifc_rel.RelatedElements if ifc_rel.is_a("IfcRelFillsElement"): source = ifc_rel.RelatingOpeningElement destinations = [ifc_rel.RelatedBuildingElement] if ifc_rel.is_a("IfcRelSpaceBoundary"): source = ifc_rel.RelatingSpace destinations = [ifc_rel.RelatedBuildingElement] if ifc_rel.is_a("IfcRelVoidsElement"): source = ifc_rel.RelatingBuildingElement destinations = [ifc_rel.RelatedOpeningElement] if source: sv = vertexAtKeyValue(vertices, key="id", value=source.id()) if sv: si = Vertex.Index(sv, vertices) for destination in destinations: if destination == None: continue ev = vertexAtKeyValue(vertices, key="id", value=destination.id()) if ev: ei = Vertex.Index(ev, vertices) if not([si,ei] in tuples or [ei,si] in tuples): tuples.append([si,ei]) e = Edge.ByVertices([sv,ev]) d = Dictionary.ByKeysValues(["id", "name", "type"], [ifc_rel.id(), ifc_rel.Name, ifc_rel.is_a()]) e = Topology.SetDictionary(e, d) edges.append(e) return edges ifc_types = IFCObjectTypes(file) ifc_objects = IFCObjects(file, include=includeTypes, exclude=excludeTypes) vertices = [] for ifc_object in ifc_objects: v = vertexByIFCObject(ifc_object, ifc_types) if v: vertices.append(v) if len(vertices) > 0: ifc_relationships = IFCRelationships(file, include=includeRels, exclude=excludeRels) edges = edgesByIFCRelationships(ifc_relationships, ifc_types, vertices) g = Graph.ByVerticesEdges(vertices, edges) else: g = None return g
def ByIFCPath(path, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5)
-
Create a Graph from an IFC path. This code is partially based on code from Bruno Postle.
Parameters
path
:str
- The input IFC file path.
includeTypes
:list
, optional- A list of IFC object types to include in the graph. The default is [] which means all object types are included.
excludeTypes
:list
, optional- A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded.
includeRels
:list
, optional- A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included.
excludeRels
:list
, optional- A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded.
xMin
:float
, optional- The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
yMin
:float
, optional- The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
zMin
:float
, optional- The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
xMax
:float
, optional- The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
yMax
:float
, optional- The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
zMax
:float
, optional- The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
Returns
topologic_core.Graph
- The created graph.
Expand source code
@staticmethod def ByIFCPath(path, includeTypes=[], excludeTypes=[], includeRels=[], excludeRels=[], xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5): """ Create a Graph from an IFC path. This code is partially based on code from Bruno Postle. Parameters ---------- path : str The input IFC file path. includeTypes : list , optional A list of IFC object types to include in the graph. The default is [] which means all object types are included. excludeTypes : list , optional A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded. includeRels : list , optional A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included. excludeRels : list , optional A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded. xMin : float, optional The desired minimum value to assign for a vertex's X coordinate. The default is -0.5. yMin : float, optional The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5. zMin : float, optional The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5. xMax : float, optional The desired maximum value to assign for a vertex's X coordinate. The default is 0.5. yMax : float, optional The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5. zMax : float, optional The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5. Returns ------- topologic_core.Graph The created graph. """ try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom except: print("Graph.ByIFCPath - Warning: Installing required ifcopenshell library.") try: os.system("pip install ifcopenshell") except: os.system("pip install ifcopenshell --user") try: import ifcopenshell import ifcopenshell.util.placement import ifcopenshell.util.element import ifcopenshell.util.shape import ifcopenshell.geom print("Graph.ByIFCPath - Warning: ifcopenshell library installed correctly.") except: warnings.warn("Graph.ByIFCPath - Error: Could not import ifcopenshell. Please try to install ifcopenshell manually. Returning None.") return None if not path: print("Graph.ByIFCPath - Error: the input path is not a valid path. Returning None.") return None ifc_file = ifcopenshell.open(path) if not ifc_file: print("Graph.ByIFCPath - Error: Could not open the IFC file. Returning None.") return None return Graph.ByIFCFile(ifc_file, includeTypes=includeTypes, excludeTypes=excludeTypes, includeRels=includeRels, excludeRels=excludeRels, xMin=xMin, yMin=yMin, zMin=zMin, xMax=xMax, yMax=yMax, zMax=zMax)
def ByMeshData(vertices, edges, vertexDictionaries=None, edgeDictionaries=None, tolerance=0.0001)
-
Creates a graph from the input mesh data
Parameters
vertices
:list
- The list of [x, y, z] coordinates of the vertices/
edges
:list
- the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j].
vertexDictionaries
:list
, optional- The python dictionaries of the vertices (in the same order as the list of vertices).
edgeDictionaries
:list
, optional- The python dictionaries of the edges (in the same order as the list of edges).
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The created graph
Expand source code
@staticmethod def ByMeshData(vertices, edges, vertexDictionaries=None, edgeDictionaries=None, tolerance=0.0001): """ Creates a graph from the input mesh data Parameters ---------- vertices : list The list of [x, y, z] coordinates of the vertices/ edges : list the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j]. vertexDictionaries : list , optional The python dictionaries of the vertices (in the same order as the list of vertices). edgeDictionaries : list , optional The python dictionaries of the edges (in the same order as the list of edges). tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The created graph """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology g_vertices = [] for i, v in enumerate(vertices): g_v = Vertex.ByCoordinates(v[0], v[1], v[2]) if not vertexDictionaries == None: if isinstance(vertexDictionaries[i], dict): d = Dictionary.ByPythonDictionary(vertexDictionaries[i]) else: d = vertexDictionaries[i] if not d == None: if len(Dictionary.Keys(d)) > 0: g_v = Topology.SetDictionary(g_v, d) g_vertices.append(g_v) g_edges = [] for i, e in enumerate(edges): sv = g_vertices[e[0]] ev = g_vertices[e[1]] g_e = Edge.ByVertices([sv, ev], tolerance=tolerance) if not edgeDictionaries == None: if isinstance(edgeDictionaries[i], dict): d = Dictionary.ByPythonDictionary(edgeDictionaries[i]) else: d = edgeDictionaries[i] if not d == None: if len(Dictionary.Keys(d)) > 0: g_e = Topology.SetDictionary(g_e, d) g_edges.append(g_e) return Graph.ByVerticesEdges(g_vertices, g_edges)
def ByTopology(topology, direct=True, directApertures=False, viaSharedTopologies=False, viaSharedApertures=False, toExteriorTopologies=False, toExteriorApertures=False, toContents=False, toOutposts=False, idKey='TOPOLOGIC_ID', outpostsKey='outposts', useInternalVertex=True, storeBREP=False, tolerance=0.0001)
-
Creates a graph.See https://en.wikipedia.org/wiki/Graph_(discrete_mathematics).
Parameters
topology
:topologic_core.Topology
- The input topology.
direct
:bool
, optional- If set to True, connect the subtopologies directly with a single edge. The default is True.
directApertures
:bool
, optional- If set to True, connect the subtopologies directly with a single edge if they share one or more apertures. The default is False.
viaSharedTopologies
:bool
, optional- If set to True, connect the subtopologies via their shared topologies. The default is False.
viaSharedApertures
:bool
, optional- If set to True, connect the subtopologies via their shared apertures. The default is False.
toExteriorTopologies
:bool
, optional- If set to True, connect the subtopologies to their exterior topologies. The default is False.
toExteriorApertures
:bool
, optional- If set to True, connect the subtopologies to their exterior apertures. The default is False.
toContents
:bool
, optional- If set to True, connect the subtopologies to their contents. The default is False.
toOutposts
:bool
, optional- If set to True, connect the topology to the list specified in its outposts. The default is False.
idKey
:str
, optional- The key to use to find outpost by ID. It is case insensitive. The default is "TOPOLOGIC_ID".
outpostsKey
:str
, optional- The key to use to find the list of outposts. It is case insensitive. The default is "outposts".
useInternalVertex
:bool
, optional- If set to True, use an internal vertex to represent the subtopology. Otherwise, use its centroid. The default is False.
storeBREP
:bool
, optional- If set to True, store the BRep of the subtopology in its representative vertex. The default is False.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The created graph.
Expand source code
@staticmethod def ByTopology(topology, direct=True, directApertures=False, viaSharedTopologies=False, viaSharedApertures=False, toExteriorTopologies=False, toExteriorApertures=False, toContents=False, toOutposts=False, idKey="TOPOLOGIC_ID", outpostsKey="outposts", useInternalVertex=True, storeBREP=False, tolerance=0.0001): """ Creates a graph.See https://en.wikipedia.org/wiki/Graph_(discrete_mathematics). Parameters ---------- topology : topologic_core.Topology The input topology. direct : bool , optional If set to True, connect the subtopologies directly with a single edge. The default is True. directApertures : bool , optional If set to True, connect the subtopologies directly with a single edge if they share one or more apertures. The default is False. viaSharedTopologies : bool , optional If set to True, connect the subtopologies via their shared topologies. The default is False. viaSharedApertures : bool , optional If set to True, connect the subtopologies via their shared apertures. The default is False. toExteriorTopologies : bool , optional If set to True, connect the subtopologies to their exterior topologies. The default is False. toExteriorApertures : bool , optional If set to True, connect the subtopologies to their exterior apertures. The default is False. toContents : bool , optional If set to True, connect the subtopologies to their contents. The default is False. toOutposts : bool , optional If set to True, connect the topology to the list specified in its outposts. The default is False. idKey : str , optional The key to use to find outpost by ID. It is case insensitive. The default is "TOPOLOGIC_ID". outpostsKey : str , optional The key to use to find the list of outposts. It is case insensitive. The default is "outposts". useInternalVertex : bool , optional If set to True, use an internal vertex to represent the subtopology. Otherwise, use its centroid. The default is False. storeBREP : bool , optional If set to True, store the BRep of the subtopology in its representative vertex. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Dictionary import Dictionary from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology from topologicpy.Aperture import Aperture def mergeDictionaries(sources): if isinstance(sources, list) == False: sources = [sources] sinkKeys = [] sinkValues = [] d = sources[0].GetDictionary() if d != None: stlKeys = d.Keys() if len(stlKeys) > 0: sinkKeys = d.Keys() sinkValues = Dictionary.Values(d) for i in range(1,len(sources)): d = sources[i].GetDictionary() if d == None: continue stlKeys = d.Keys() if len(stlKeys) > 0: sourceKeys = d.Keys() for aSourceKey in sourceKeys: if aSourceKey not in sinkKeys: sinkKeys.append(aSourceKey) sinkValues.append("") for i in range(len(sourceKeys)): index = sinkKeys.index(sourceKeys[i]) sourceValue = Dictionary.ValueAtKey(d, sourceKeys[i]) if sourceValue != None: if sinkValues[index] != "": if isinstance(sinkValues[index], list): sinkValues[index].append(sourceValue) else: sinkValues[index] = [sinkValues[index], sourceValue] else: sinkValues[index] = sourceValue if len(sinkKeys) > 0 and len(sinkValues) > 0: return Dictionary.ByKeysValues(sinkKeys, sinkValues) return None def mergeDictionaries2(sources): if isinstance(sources, list) == False: sources = [sources] sinkKeys = [] sinkValues = [] d = sources[0] if d != None: stlKeys = d.Keys() if len(stlKeys) > 0: sinkKeys = d.Keys() sinkValues = Dictionary.Values(d) for i in range(1,len(sources)): d = sources[i] if d == None: continue stlKeys = d.Keys() if len(stlKeys) > 0: sourceKeys = d.Keys() for aSourceKey in sourceKeys: if aSourceKey not in sinkKeys: sinkKeys.append(aSourceKey) sinkValues.append("") for i in range(len(sourceKeys)): index = sinkKeys.index(sourceKeys[i]) sourceValue = Dictionary.ValueAtKey(d, sourceKeys[i]) if sourceValue != None: if sinkValues[index] != "": if isinstance(sinkValues[index], list): sinkValues[index].append(sourceValue) else: sinkValues[index] = [sinkValues[index], sourceValue] else: sinkValues[index] = sourceValue if len(sinkKeys) > 0 and len(sinkValues) > 0: return Dictionary.ByKeysValues(sinkKeys, sinkValues) return None def outpostsByID(topologies, ids, idKey="TOPOLOGIC_ID"): returnList = [] idList = [] for t in topologies: d = Topology.Dictionary(t) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == idKey.lower(): k = key if k: id = Dictionary.ValueAtKey(d, k) else: id = "" idList.append(id) for id in ids: try: index = idList.index(id) except: index = None if index: returnList.append(topologies[index]) return returnList def processCellComplex(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item edges = [] vertices = [] cellmat = [] if direct == True: cells = [] _ = topology.Cells(None, cells) # Create a matrix of zeroes for i in range(len(cells)): cellRow = [] for j in range(len(cells)): cellRow.append(0) cellmat.append(cellRow) for i in range(len(cells)): for j in range(len(cells)): if (i != j) and cellmat[i][j] == 0: cellmat[i][j] = 1 cellmat[j][i] = 1 sharedt = Topology.SharedFaces(cells[i], cells[j]) if len(sharedt) > 0: if useInternalVertex == True: v1 = Topology.InternalVertex(cells[i], tolerance=tolerance) v2 = Topology.InternalVertex(cells[j], tolerance=tolerance) else: v1 = cells[i].CenterOfMass() v2 = cells[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(sharedt) if not mDict == None: keys = (Dictionary.Keys(mDict) or [])+["relationship"] values = (Dictionary.Values(mDict) or [])+["Direct"] else: keys = ["relationship"] values = ["Direct"] mDict = Dictionary.ByKeysValues(keys, values) if mDict: e.SetDictionary(mDict) edges.append(e) if directApertures == True: cellmat = [] cells = [] _ = topology.Cells(None, cells) # Create a matrix of zeroes for i in range(len(cells)): cellRow = [] for j in range(len(cells)): cellRow.append(0) cellmat.append(cellRow) for i in range(len(cells)): for j in range(len(cells)): if (i != j) and cellmat[i][j] == 0: cellmat[i][j] = 1 cellmat[j][i] = 1 sharedt = Topology.SharedFaces(cells[i], cells[j]) if len(sharedt) > 0: apertureExists = False for x in sharedt: apList = [] _ = x.Apertures(apList) if len(apList) > 0: apTopList = [] for ap in apList: apTopList.append(ap.Topology()) apertureExists = True break if apertureExists: if useInternalVertex == True: v1 = Topology.InternalVertex(cells[i], tolerance=tolerance) v2 = Topology.InternalVertex(cells[j], tolerance=tolerance) else: v1 = cells[i].CenterOfMass() v2 = cells[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(apTopList) if mDict: e.SetDictionary(mDict) edges.append(e) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance=tolerance) vcc = Topology.InternalVertex(topology, tolerance=tolerance) else: vop = Topology.CenterOfMass(outpost) vcc = Topology.CenterOfMass(topology) d1 = Topology.Dictionary(vcc) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vcc.SetDictionary(d3) else: _ = vcc.SetDictionary(d1) vertices.append(vcc) tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) cells = [] _ = topology.Cells(None, cells) if any([viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents]): for aCell in cells: if useInternalVertex == True: vCell = Topology.InternalVertex(aCell, tolerance=tolerance) else: vCell = aCell.CenterOfMass() d1 = aCell.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aCell), Topology.Type(aCell), Topology.TypeAsString(aCell)]) d3 = mergeDictionaries2([d1, d2]) _ = vCell.SetDictionary(d3) else: _ = vCell.SetDictionary(d1) vertices.append(vCell) faces = [] _ = aCell.Faces(None, faces) sharedTopologies = [] exteriorTopologies = [] sharedApertures = [] exteriorApertures = [] contents = [] _ = aCell.Contents(contents) for aFace in faces: cells1 = [] _ = aFace.Cells(topology, cells1) if len(cells1) > 1: sharedTopologies.append(aFace) apertures = [] _ = aFace.Apertures(apertures) for anAperture in apertures: sharedApertures.append(anAperture) else: exteriorTopologies.append(aFace) apertures = [] _ = aFace.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if viaSharedTopologies: for sharedTopology in sharedTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(sharedTopology, tolerance) else: vst = sharedTopology.CenterOfMass() d1 = sharedTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = sharedTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() d1 = content.GetDictionary() vst2 = Vertex.ByCoordinates(vst2.X(), vst2.Y(), vst2.Z()) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if viaSharedApertures: for sharedAperture in sharedApertures: sharedAp = sharedAperture.Topology() if useInternalVertex == True: vsa = Topology.InternalVertex(sharedAp, tolerance) else: vsa = sharedAp.CenterOfMass() d1 = sharedAp.GetDictionary() vsa = Vertex.ByCoordinates(vsa.X()+(tolerance*100), vsa.Y()+(tolerance*100), vsa.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)]) d3 = mergeDictionaries2([d1, d2]) _ = vsa.SetDictionary(d3) else: _ = vsa.SetDictionary(d1) vertices.append(vsa) tempe = Edge.ByStartVertexEndVertex(vCell, vsa, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vet = Topology.InternalVertex(exteriorTopology, tolerance) else: vet = exteriorTopology.CenterOfMass() _ = vet.SetDictionary(exteriorTopology.GetDictionary()) d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vet.SetDictionary(d3) else: _ = vet.SetDictionary(d1) vertices.append(vet) tempe = Edge.ByStartVertexEndVertex(vCell, vet, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() d1 = content.GetDictionary() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vet, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vea = Topology.InternalVertex(exTop, tolerance) else: vea = exTop.CenterOfMass() d1 = exTop.GetDictionary() vea = Vertex.ByCoordinates(vea.X()+(tolerance*100), vea.Y()+(tolerance*100), vea.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vea.SetDictionary(d3) else: _ = vea.SetDictionary(d1) vertices.append(vea) tempe = Edge.ByStartVertexEndVertex(vCell, vea, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = aCell.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vcn = Topology.InternalVertex(content, tolerance) else: vcn = content.CenterOfMass() vcn = Vertex.ByCoordinates(vcn.X()+(tolerance*100), vcn.Y()+(tolerance*100), vcn.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vcn.SetDictionary(d3) else: _ = vcn.SetDictionary(d1) vertices.append(vcn) tempe = Edge.ByStartVertexEndVertex(vCell, vcn, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) for aCell in cells: if useInternalVertex == True: vCell = Topology.InternalVertex(aCell, tolerance=tolerance) else: vCell = aCell.CenterOfMass() d1 = aCell.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aCell), Topology.Type(aCell), Topology.TypeAsString(aCell)]) d3 = mergeDictionaries2([d1, d2]) _ = vCell.SetDictionary(d3) else: _ = vCell.SetDictionary(d1) vertices.append(vCell) return [vertices,edges] def processCell(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item vertices = [] edges = [] if useInternalVertex == True: vCell = Topology.InternalVertex(topology, tolerance=tolerance) else: vCell = topology.CenterOfMass() d1 = topology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vCell.SetDictionary(d3) else: _ = vCell.SetDictionary(d1) vertices.append(vCell) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(vCell, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if any([toExteriorTopologies, toExteriorApertures, toContents]): faces = Topology.Faces(topology) exteriorTopologies = [] exteriorApertures = [] for aFace in faces: exteriorTopologies.append(aFace) apertures = Topology.Apertures(aFace) for anAperture in apertures: exteriorApertures.append(anAperture) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = topology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vCell, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processShell(item): from topologicpy.Face import Face topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None edges = [] vertices = [] facemat = [] if direct == True: topFaces = [] _ = topology.Faces(None, topFaces) # Create a matrix of zeroes for i in range(len(topFaces)): faceRow = [] for j in range(len(topFaces)): faceRow.append(0) facemat.append(faceRow) for i in range(len(topFaces)): for j in range(len(topFaces)): if (i != j) and facemat[i][j] == 0: facemat[i][j] = 1 facemat[j][i] = 1 sharedt = Topology.SharedEdges(topFaces[i], topFaces[j]) if len(sharedt) > 0: if useInternalVertex == True: v1 = Topology.InternalVertex(topFaces[i], tolerance=tolerance) v2 = Topology.InternalVertex(topFaces[j], tolerance=tolerance) else: v1 = topFaces[i].CenterOfMass() v2 = topFaces[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(sharedt) if not mDict == None: keys = (Dictionary.Keys(mDict) or [])+["relationship"] values = (Dictionary.Values(mDict) or [])+["Direct"] else: keys = ["relationship"] values = ["Direct"] mDict = Dictionary.ByKeysValues(keys, values) if mDict: e.SetDictionary(mDict) edges.append(e) if directApertures == True: facemat = [] topFaces = [] _ = topology.Faces(None, topFaces) # Create a matrix of zeroes for i in range(len(topFaces)): faceRow = [] for j in range(len(topFaces)): faceRow.append(0) facemat.append(faceRow) for i in range(len(topFaces)): for j in range(len(topFaces)): if (i != j) and facemat[i][j] == 0: facemat[i][j] = 1 facemat[j][i] = 1 sharedt = Topology.SharedEdges(topFaces[i], topFaces[j]) if len(sharedt) > 0: apertureExists = False for x in sharedt: apList = [] _ = x.Apertures(apList) if len(apList) > 0: apertureExists = True break if apertureExists: apTopList = [] for ap in apList: apTopList.append(ap.Topology()) if useInternalVertex == True: v1 = Topology.InternalVertex(topFaces[i], tolerance=tolerance) v2 = Topology.InternalVertex(topFaces[j], tolerance=tolerance) else: v1 = topFaces[i].CenterOfMass() v2 = topFaces[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(apTopList) if mDict: e.SetDictionary(mDict) edges.append(e) topFaces = [] _ = topology.Faces(None, topFaces) if any([viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents == True]): for aFace in topFaces: if useInternalVertex == True: vFace = Topology.InternalVertex(aFace, tolerance=tolerance) else: vFace = aFace.CenterOfMass() _ = vFace.SetDictionary(aFace.GetDictionary()) vertices.append(vFace) fEdges = [] _ = aFace.Edges(None, fEdges) sharedTopologies = [] exteriorTopologies = [] sharedApertures = [] exteriorApertures = [] for anEdge in fEdges: faces = [] _ = anEdge.Faces(topology, faces) if len(faces) > 1: sharedTopologies.append(anEdge) apertures = [] _ = anEdge.Apertures(apertures) for anAperture in apertures: sharedApertures.append(anAperture) else: exteriorTopologies.append(anEdge) apertures = [] _ = anEdge.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if viaSharedTopologies: for sharedTopology in sharedTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(sharedTopology, tolerance) else: vst = sharedTopology.CenterOfMass() d1 = sharedTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = sharedTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if viaSharedApertures: for sharedAperture in sharedApertures: sharedAp = Aperture.Topology(sharedAperture) if useInternalVertex == True: vst = Topology.InternalVertex(sharedAp, tolerance) else: vst = sharedAp.CenterOfMass() d1 = sharedAp.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = aFace.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) for aFace in topFaces: if useInternalVertex == True: vFace = Topology.InternalVertex(aFace, tolerance) else: vFace = aFace.CenterOfMass() d1 = aFace.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(aFace), Topology.Type(aFace), Topology.TypeAsString(aFace)]) d3 = mergeDictionaries2([d1, d2]) _ = vFace.SetDictionary(d3) else: _ = vFace.SetDictionary(d1) vertices.append(vFace) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) vcc = Topology.InternalVertex(topology, tolerance) else: vop = Topology.CenterOfMass(outpost) vcc = Topology.CenterOfMass(topology) d1 = Topology.Dictionary(vcc) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vcc.SetDictionary(d3) else: _ = vcc.SetDictionary(d1) vertices.append(vcc) tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processFace(item): from topologicpy.Face import Face topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None vertices = [] edges = [] if useInternalVertex == True: vFace = Topology.InternalVertex(topology, tolerance=tolerance) else: vFace = topology.CenterOfMass() d1 = topology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vFace.SetDictionary(d3) else: _ = vFace.SetDictionary(d1) vertices.append(vFace) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(vFace, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True): fEdges = [] _ = topology.Edges(None, fEdges) exteriorTopologies = [] exteriorApertures = [] for anEdge in fEdges: exteriorTopologies.append(anEdge) apertures = [] _ = anEdge.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = exteriorTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = topology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vFace, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processWire(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None edges = [] vertices = [] edgemat = [] if direct == True: topEdges = [] _ = topology.Edges(None, topEdges) # Create a matrix of zeroes for i in range(len(topEdges)): edgeRow = [] for j in range(len(topEdges)): edgeRow.append(0) edgemat.append(edgeRow) for i in range(len(topEdges)): for j in range(len(topEdges)): if (i != j) and edgemat[i][j] == 0: edgemat[i][j] = 1 edgemat[j][i] = 1 sharedt = Topology.SharedVertices(topEdges[i], topEdges[j]) if len(sharedt) > 0: try: v1 = Edge.VertexByParameter(topEdges[i], 0.5) except: v1 = topEdges[j].CenterOfMass() try: v2 = Edge.VertexByParameter(topEdges[j], 0.5) except: v2 = topEdges[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) mDict = mergeDictionaries(sharedt) if not mDict == None: keys = (Dictionary.Keys(mDict) or [])+["relationship"] values = (Dictionary.Values(mDict) or [])+["Direct"] else: keys = ["relationship"] values = ["Direct"] mDict = Dictionary.ByKeysValues(keys, values) if mDict: e.SetDictionary(mDict) edges.append(e) if directApertures == True: edgemat = [] topEdges = [] _ = topology.Edges(None, topEdges) # Create a matrix of zeroes for i in range(len(topEdges)): edgeRow = [] for j in range(len(topEdges)): edgeRow.append(0) edgemat.append(edgeRow) for i in range(len(topEdges)): for j in range(len(topEdges)): if (i != j) and edgemat[i][j] == 0: edgemat[i][j] = 1 edgemat[j][i] = 1 sharedt = Topology.SharedVertices(topEdges[i], topEdges[j]) if len(sharedt) > 0: apertureExists = False for x in sharedt: apList = [] _ = x.Apertures(apList) if len(apList) > 0: apertureExists = True break if apertureExists: try: v1 = Edge.VertexByParameter(topEdges[i], 0.5) except: v1 = topEdges[j].CenterOfMass() try: v2 = Edge.VertexByParameter(topEdges[j], 0.5) except: v2 = topEdges[j].CenterOfMass() e = Edge.ByStartVertexEndVertex(v1, v2, tolerance=tolerance) apTopologies = [] for ap in apList: apTopologies.append(ap.Topology()) mDict = mergeDictionaries(apTopologies) if mDict: e.SetDictionary(mDict) edges.append(e) topEdges = [] _ = topology.Edges(None, topEdges) if (viaSharedTopologies == True) or (viaSharedApertures == True) or (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True): for anEdge in topEdges: try: vEdge = Edge.VertexByParameter(anEdge, 0.5) except: vEdge = anEdge.CenterOfMass() d1 = anEdge.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(anEdge), Topology.Type(anEdge), Topology.TypeAsString(anEdge)]) d3 = mergeDictionaries2([d1, d2]) _ = vEdge.SetDictionary(d3) else: _ = vEdge.SetDictionary(d1) vertices.append(vEdge) eVertices = [] _ = anEdge.Vertices(None, eVertices) sharedTopologies = [] exteriorTopologies = [] sharedApertures = [] exteriorApertures = [] contents = [] _ = anEdge.Contents(contents) for aVertex in eVertices: tempEdges = [] _ = aVertex.Edges(topology, tempEdges) if len(tempEdges) > 1: sharedTopologies.append(aVertex) apertures = [] _ = aVertex.Apertures(apertures) for anAperture in apertures: sharedApertures.append(anAperture) else: exteriorTopologies.append(aVertex) apertures = [] _ = aVertex.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if viaSharedTopologies: for sharedTopology in sharedTopologies: vst = sharedTopology.CenterOfMass() d1 = sharedTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedTopology), Topology.Type(sharedTopology), Topology.TypeAsString(sharedTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = sharedTopology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if viaSharedApertures: for sharedAperture in sharedApertures: sharedAp = sharedAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(sharedAp, tolerance) else: vst = sharedAp.CenterOfMass() d1 = sharedAp.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(sharedAp), Topology.Type(sharedAp), Topology.TypeAsString(sharedAp)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["Via Shared Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: vst = exteriorTopology vertices.append(exteriorTopology) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = vst.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = anEdge.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) for anEdge in topEdges: try: vEdge = Edge.VertexByParameter(anEdge, 0.5) except: vEdge = anEdge.CenterOfMass() d1 = anEdge.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(anEdge), Topology.Type(anEdge), Topology.TypeAsString(anEdge)]) d3 = mergeDictionaries2([d1, d2]) _ = vEdge.SetDictionary(d3) else: _ = vEdge.SetDictionary(d1) vertices.append(vEdge) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) vcc = Topology.InternalVertex(topology, tolerance) else: vop = Topology.CenterOfMass(outpost) vcc = Topology.CenterOfMass(topology) d1 = Topology.Dictionary(vcc) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vcc.SetDictionary(d3) else: _ = vcc.SetDictionary(d1) vertices.append(vcc) tempe = Edge.ByStartVertexEndVertex(vcc, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processEdge(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item graph = None vertices = [] edges = [] if useInternalVertex == True: try: vEdge = Edge.VertexByParameter(topology, 0.5) except: vEdge = topology.CenterOfMass() else: vEdge = topology.CenterOfMass() d1 = vEdge.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(topology), Topology.Type(topology), Topology.TypeAsString(topology)]) d3 = mergeDictionaries2([d1, d2]) _ = vEdge.SetDictionary(d3) else: _ = vEdge.SetDictionary(topology.GetDictionary()) vertices.append(vEdge) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(vEdge, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if (toExteriorTopologies == True) or (toExteriorApertures == True) or (toContents == True): eVertices = [] _ = topology.Vertices(None, eVertices) exteriorTopologies = [] exteriorApertures = [] for aVertex in eVertices: exteriorTopologies.append(aVertex) apertures = [] _ = aVertex.Apertures(apertures) for anAperture in apertures: exteriorApertures.append(anAperture) if toExteriorTopologies: for exteriorTopology in exteriorTopologies: if useInternalVertex == True: vst = Topology.InternalVertex(exteriorTopology, tolerance) else: vst = exteriorTopology.CenterOfMass() d1 = exteriorTopology.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exteriorTopology), Topology.Type(exteriorTopology), Topology.TypeAsString(exteriorTopology)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Topologies"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toContents: contents = [] _ = vst.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst2 = Topology.InternalVertex(content, tolerance) else: vst2 = content.CenterOfMass() vst2 = Vertex.ByCoordinates(vst2.X()+(tolerance*100), vst2.Y()+(tolerance*100), vst2.Z()+(tolerance*100)) d1 = content.GetDictionary() if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst2.SetDictionary(d3) else: _ = vst2.SetDictionary(d1) vertices.append(vst2) tempe = Edge.ByStartVertexEndVertex(vst, vst2, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toExteriorApertures: for exteriorAperture in exteriorApertures: exTop = exteriorAperture.Topology() if useInternalVertex == True: vst = Topology.InternalVertex(exTop, tolerance) else: vst = exTop.CenterOfMass() d1 = exTop.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(exTop), Topology.Type(exTop), Topology.TypeAsString(exTop)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) _ = vst.SetDictionary(exTop.GetDictionary()) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(vEdge, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Exterior Apertures"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] def processVertex(item): topology, others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance = item vertices = [topology] edges = [] if toContents: contents = [] _ = topology.Contents(contents) for content in contents: if Topology.IsInstance(content, "Aperture"): content = Aperture.Topology(content) if useInternalVertex == True: vst = Topology.InternalVertex(content, tolerance) else: vst = content.CenterOfMass() d1 = content.GetDictionary() vst = Vertex.ByCoordinates(vst.X()+(tolerance*100), vst.Y()+(tolerance*100), vst.Z()+(tolerance*100)) if storeBREP: d2 = Dictionary.ByKeysValues(["brep", "brepType", "brepTypeString"], [Topology.BREPString(content), Topology.Type(content), Topology.TypeAsString(content)]) d3 = mergeDictionaries2([d1, d2]) _ = vst.SetDictionary(d3) else: _ = vst.SetDictionary(d1) vertices.append(vst) tempe = Edge.ByStartVertexEndVertex(topology, vst, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Contents"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) if toOutposts and others: d = Topology.Dictionary(topology) if not d == None: keys = Dictionary.Keys(d) else: keys = [] k = None for key in keys: if key.lower() == outpostsKey.lower(): k = key if k: ids = Dictionary.ValueAtKey(d, k) outposts = outpostsByID(others, ids, idKey) else: outposts = [] for outpost in outposts: if useInternalVertex == True: vop = Topology.InternalVertex(outpost, tolerance) else: vop = Topology.CenterOfMass(outpost) tempe = Edge.ByStartVertexEndVertex(topology, vop, tolerance=tolerance) tempd = Dictionary.ByKeysValues(["relationship"],["To Outposts"]) _ = tempe.SetDictionary(tempd) edges.append(tempe) return [vertices, edges] if not Topology.IsInstance(topology, "Topology"): print("Graph.ByTopology - Error: The input topology is not a valid topology. Returning None.") return None graph = None item = [topology, None, None, None, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, None, useInternalVertex, storeBREP, tolerance] vertices = [] edges = [] if Topology.IsInstance(topology, "CellComplex"): vertices, edges = processCellComplex(item) elif Topology.IsInstance(topology, "Cell"): vertices, edges = processCell(item) elif Topology.IsInstance(topology, "Shell"): vertices, edges = processShell(item) elif Topology.IsInstance(topology, "Face"): vertices, edges = processFace(item) elif Topology.IsInstance(topology, "Wire"): vertices, edges = processWire(item) elif Topology.IsInstance(topology, "Edge"): vertices, edges = processEdge(item) elif Topology.IsInstance(topology, "Vertex"): vertices, edges = processVertex(item) elif Topology.IsInstance(topology, "Cluster"): c_cellComplexes = Topology.CellComplexes(topology) c_cells = Cluster.FreeCells(topology, tolerance=tolerance) c_shells = Cluster.FreeShells(topology, tolerance=tolerance) c_faces = Cluster.FreeFaces(topology, tolerance=tolerance) c_wires = Cluster.FreeWires(topology, tolerance=tolerance) c_edges = Cluster.FreeEdges(topology, tolerance=tolerance) c_vertices = Cluster.FreeVertices(topology, tolerance=tolerance) others = c_cellComplexes+c_cells+c_shells+c_faces+c_wires+c_edges+c_vertices parameters = [others, outpostsKey, idKey, direct, directApertures, viaSharedTopologies, viaSharedApertures, toExteriorTopologies, toExteriorApertures, toContents, toOutposts, useInternalVertex, storeBREP, tolerance] for t in c_cellComplexes: v, e = processCellComplex([t]+parameters) vertices += v edges += e for t in c_cells: v, e = processCell([t]+parameters) vertices += v edges += e for t in c_shells: v, e = processShell([t]+parameters) vertices += v edges += e for t in c_faces: v, e = processFace([t]+parameters) vertices += v edges += e for t in c_wires: v, e = processWire([t]+parameters) vertices += v edges += e for t in c_edges: v, e = processEdge([t]+parameters) vertices += v edges += e for t in c_vertices: v, e = processVertex([t]+parameters) vertices += v edges += e else: return None return Graph.ByVerticesEdges(vertices, edges)
def ByVerticesEdges(vertices, edges)
-
Creates a graph from the input list of vertices and edges.
Parameters
vertices
:list
- The input list of vertices.
edges
:list
- The input list of edges.
Returns
topologic_core.Graph
- The created graph.
Expand source code
@staticmethod def ByVerticesEdges(vertices, edges): """ Creates a graph from the input list of vertices and edges. Parameters ---------- vertices : list The input list of vertices. edges : list The input list of edges. Returns ------- topologic_core.Graph The created graph. """ from topologicpy.Topology import Topology if not isinstance(vertices, list): print("Graph.ByVerticesEdges - Error: The input list of vertices is not a valid list. Returning None.") return None if not isinstance(edges, list): print("Graph.ByVerticesEdges - Error: The input list of edges is not a valid list. Returning None.") return None vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] edges = [e for e in edges if Topology.IsInstance(e, "Edge")] return topologic.Graph.ByVerticesEdges(vertices, edges) # Hook to Core
def ChromaticNumber(graph, maxColors: int = 3, silent: bool = False)
-
Returns the chromatic number of the input graph. See https://en.wikipedia.org/wiki/Graph_coloring.
Parameters
graph
:topologic_core.Graph
- The input graph.
maxColors
:int
, optional- The desired maximum number of colors to test against. The default is 3.
silent
:bool
, optional- If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
Returns
int
- The chromatic number of the input graph.
Expand source code
@staticmethod def ChromaticNumber(graph, maxColors: int = 3, silent: bool = False): """ Returns the chromatic number of the input graph. See https://en.wikipedia.org/wiki/Graph_coloring. Parameters ---------- graph : topologic_core.Graph The input graph. maxColors : int , optional The desired maximum number of colors to test against. The default is 3. silent : bool , optional If set to False, error and warning messages are printed. Otherwise, they are not. The default is False. Returns ------- int The chromatic number of the input graph. """ # This is based on code from https://www.geeksforgeeks.org/graph-coloring-applications/ from topologicpy.Topology import Topology def is_safe(graph, v, color, c): for i in range(len(graph)): if graph[v][i] == 1 and color[i] == c: return False return True def graph_coloring(graph, m, color, v): V = len(graph) if v == V: return True for c in range(1, m + 1): if is_safe(graph, v, color, c): color[v] = c if graph_coloring(graph, m, color, v + 1): return True color[v] = 0 return False def chromatic_number(graph): V = len(graph) color = [0] * V m = 1 while True: if graph_coloring(graph, m, color, 0): return m m += 1 if not Topology.IsInstance(graph, "Graph"): if not silent: print("Graph.ChromaticNumber - Error: The input graph parameter is not a valid graph. Returning None.") return None if maxColors < 1: if not silent: print("Graph.ChromaticNumber - Error: The input maxColors parameter is not a valid positive number. Returning None.") return None adj_matrix = Graph.AdjacencyMatrix(graph) return chromatic_number(adj_matrix)
def ClosenessCentrality(graph, vertices=None, tolerance=0.0001)
-
Return the closeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the closeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Closeness_centrality.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertices
:list
, optional- The input list of vertices. The default is None.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
Expand source code
@staticmethod def ClosenessCentrality(graph, vertices=None, tolerance = 0.0001): """ Return the closeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the closeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Closeness_centrality. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.") return None graphVertices = Graph.Vertices(graph) if not isinstance(vertices, list): vertices = graphVertices else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.ClosenessCentrality - Error: The input list of vertices does not contain any valid vertices. Returning None.") return None n = len(graphVertices) returnList = [] try: for va in tqdm(vertices, desc="Computing Closeness Centrality", leave=False): top_dist = 0 for vb in graphVertices: if Topology.IsSame(va, vb): d = 0 else: d = Graph.TopologicalDistance(graph, va, vb, tolerance) top_dist += d if top_dist == 0: returnList.append(0) else: returnList.append((n-1)/top_dist) except: print("Graph.ClosenessCentrality - Warning: Could not use tqdm.") for va in vertices: top_dist = 0 for vb in graphVertices: if Topology.IsSame(va, vb): d = 0 else: d = Graph.TopologicalDistance(graph, va, vb, tolerance) top_dist += d if top_dist == 0: returnList.append(0) else: returnList.append((n-1)/top_dist) return returnList
def Color(graph, oldKey: str = 'color', newKey: str = 'color', maxColors: int = None, tolerance: float = 0.0001)
-
Colors the input vertices within the input graph. The saved value is an integer rather than an actual color. See Color.ByValueInRange to convert to an actual color. Any vertices that have been pre-colored will not be affected. See https://en.wikipedia.org/wiki/Graph_coloring.
Parameters
graph
:topologic_core.Graph
- The input graph.
oldKey
:str
, optional- The existing dictionary key to use to read any pre-existing color information. The default is "color".
newKey
:str
, optional- The new dictionary key to use to write out new color information. The default is "color".
maxColors
:int
, optional- The desired maximum number of colors to use. If set to None, the chromatic number of the graph is used. The default is None.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph, but with its vertices colored.
Expand source code
@staticmethod def Color(graph, oldKey: str = "color", newKey: str = "color", maxColors: int = None, tolerance: float = 0.0001): """ Colors the input vertices within the input graph. The saved value is an integer rather than an actual color. See Color.ByValueInRange to convert to an actual color. Any vertices that have been pre-colored will not be affected. See https://en.wikipedia.org/wiki/Graph_coloring. Parameters ---------- graph : topologic_core.Graph The input graph. oldKey : str , optional The existing dictionary key to use to read any pre-existing color information. The default is "color". newKey : str , optional The new dictionary key to use to write out new color information. The default is "color". maxColors : int , optional The desired maximum number of colors to use. If set to None, the chromatic number of the graph is used. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph, but with its vertices colored. """ from topologicpy.Vertex import Vertex from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import math def is_safe(v, graph, colors, c): # Check if the color 'c' is safe for the vertex 'v' for i in range(len(graph)): if graph[v][i] and c == colors[i]: return False return True def graph_coloring_util(graph, m, colors, v): # Base case: If all vertices are assigned a color, return true if v == len(graph): return True # Try different colors for the current vertex 'v' for c in range(1, m + 1): # Check if assignment of color 'c' to 'v' is fine if is_safe(v, graph, colors, c): colors[v] = c # Recur to assign colors to the rest of the vertices if graph_coloring_util(graph, m, colors, v + 1): return True # If assigning color 'c' doesn't lead to a solution, remove it colors[v] = 0 # If no color can be assigned to this vertex, return false return False def graph_coloring(graph, m, colors): # Call graph_coloring_util() for vertex 0 if not graph_coloring_util(graph, m, colors, 0): return None return [x-1 for x in colors] if not Topology.IsInstance(graph, "Graph"): print("Graph.Color - Error: The input graph is not a valid graph. Returning None.") return None vertices = Graph.Vertices(graph) adj_mat = Graph.AdjacencyMatrix(graph) # Isolate vertices that have pre-existing colors as they shouldn't affect graph coloring. for i, v in enumerate(vertices): d = Topology.Dictionary(v) c = Dictionary.ValueAtKey(d, oldKey) if not c == None: adj_mat[i] = [0] * len(vertices) for j in range(len(adj_mat)): row = adj_mat[j] row[i] = 0 temp_graph = Graph.ByAdjacencyMatrix(adj_mat) # If the maximum number of colors are not provided, compute it using the graph's chromatic number. if maxColors == None: maxColors = Graph.ChromaticNumber(temp_graph) colors = [0] * len(vertices) colors = graph_coloring(adj_mat, maxColors, colors) for i, v in enumerate(vertices): d = Topology.Dictionary(v) d = Dictionary.SetValueAtKey(d, newKey, colors[i]) v = Topology.SetDictionary(v, d) return graph
def Connect(graph, verticesA, verticesB, tolerance=0.0001)
-
Connects the two lists of input vertices.
Parameters
graph
:topologic_core.Graph
- The input graph.
verticesA
:list
- The first list of input vertices.
verticesB
:topologic_core.Vertex
- The second list of input vertices.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph with the connected input vertices.
Expand source code
@staticmethod def Connect(graph, verticesA, verticesB, tolerance=0.0001): """ Connects the two lists of input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. verticesA : list The first list of input vertices. verticesB : topologic_core.Vertex The second list of input vertices. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the connected input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Connect - Error: The input graph is not a valid graph. Returning None.") return None if not isinstance(verticesA, list): print("Graph.Connect - Error: The input list of verticesA is not a valid list. Returning None.") return None if not isinstance(verticesB, list): print("Graph.Connect - Error: The input list of verticesB is not a valid list. Returning None.") return None verticesA = [v for v in verticesA if Topology.IsInstance(v, "Vertex")] verticesB = [v for v in verticesB if Topology.IsInstance(v, "Vertex")] if len(verticesA) < 1: print("Graph.Connect - Error: The input list of verticesA does not contain any valid vertices. Returning None.") return None if len(verticesB) < 1: print("Graph.Connect - Error: The input list of verticesB does not contain any valid vertices. Returning None.") return None if not len(verticesA) == len(verticesB): print("Graph.Connect - Error: The input lists verticesA and verticesB have different lengths. Returning None.") return None _ = graph.Connect(verticesA, verticesB, tolerance) return graph
def ContainsEdge(graph, edge, tolerance=0.0001)
-
Returns True if the input graph contains the input edge. Returns False otherwise.
Parameters
graph
:topologic_core.Graph
- The input graph.
edge
:topologic_core.Edge
- The input edge.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
bool
- True if the input graph contains the input edge. False otherwise.
Expand source code
@staticmethod def ContainsEdge(graph, edge, tolerance=0.0001): """ Returns True if the input graph contains the input edge. Returns False otherwise. Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input edge. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- bool True if the input graph contains the input edge. False otherwise. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ContainsEdge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.ContainsEdge - Error: The input edge is not a valid edge. Returning None.") return None return graph.ContainsEdge(edge, tolerance)
def ContainsVertex(graph, vertex, tolerance=0.0001)
-
Returns True if the input graph contains the input Vertex. Returns False otherwise.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input Vertex.
tolerance
:float
, optional- Ther desired tolerance. The default is 0.0001.
Returns
bool
- True if the input graph contains the input vertex. False otherwise.
Expand source code
@staticmethod def ContainsVertex(graph, vertex, tolerance=0.0001): """ Returns True if the input graph contains the input Vertex. Returns False otherwise. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input Vertex. tolerance : float , optional Ther desired tolerance. The default is 0.0001. Returns ------- bool True if the input graph contains the input vertex. False otherwise. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ContainsVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.ContainsVertex - Error: The input vertex is not a valid vertex. Returning None.") return None return graph.ContainsVertex(vertex, tolerance)
def ContractEdge(graph, edge, vertex=None, tolerance=0.0001)
-
Contracts the input edge in the input graph into a single vertex. Please note that the dictionary of the edge is transferred to the vertex that replaces it. See https://en.wikipedia.org/wiki/Edge_contraction
Parameters
graph
:topologic_core.Graph
- The input graph.
edge
:topologic_core.Edge
- The input graph edge that needs to be contracted.
vertex
:topollogic.Vertex
, optional- The vertex to replace the contracted edge. If set to None, the centroid of the edge is chosen. The default is None.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph, but with input edge contracted into a single vertex.
Expand source code
@staticmethod def ContractEdge(graph, edge, vertex=None, tolerance=0.0001): """ Contracts the input edge in the input graph into a single vertex. Please note that the dictionary of the edge is transferred to the vertex that replaces it. See https://en.wikipedia.org/wiki/Edge_contraction Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input graph edge that needs to be contracted. vertex : topollogic.Vertex , optional The vertex to replace the contracted edge. If set to None, the centroid of the edge is chosen. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph, but with input edge contracted into a single vertex. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary def OppositeVertex(edge, vertex, tolerance=0.0001): sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) d1 = Vertex.Distance(vertex, sv) d2 = Vertex.Distance(vertex, ev) if d1 < d2: return [ev, 1] return [sv, 0] if not Topology.IsInstance(graph, "Graph"): print("Graph.ContractEdge - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.ContractEdge - Error: The input edge parameter is not a valid edge. Returning None.") return None if vertex == None: vertex = Topology.Centroid(edge) sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) vd = Topology.Dictionary(vertex) sd = Topology.Dictionary(sv) dictionaries = [] keys = Dictionary.Keys(vd) if isinstance(keys, list): if len(keys) > 0: dictionaries.append(vd) keys = Dictionary.Keys(sd) if isinstance(keys, list): if len(keys) > 0: dictionaries.append(sd) ed = Topology.Dictionary(ev) keys = Dictionary.Keys(ed) if isinstance(keys, list): if len(keys) > 0: dictionaries.append(ed) if len(dictionaries) == 1: vertex = Topology.SetDictionary(vertex, dictionaries[0]) elif len(dictionaries) > 1: cd = Dictionary.ByMergedDictionaries(dictionaries) vertex = Topology.SetDictionary(vertex, cd) graph = Graph.RemoveEdge(graph, edge, tolerance=tolerance) graph = Graph.AddVertex(graph, vertex, tolerance=tolerance) adj_edges_sv = Graph.Edges(graph, [sv]) adj_edges_ev = Graph.Edges(graph, [ev]) new_edges = [] for adj_edge_sv in adj_edges_sv: ov, flag = OppositeVertex(adj_edge_sv, sv) if flag == 0: new_edge = Edge.ByVertices([ov, vertex]) else: new_edge = Edge.ByVertices([vertex, ov]) d = Topology.Dictionary(adj_edge_sv) keys = Dictionary.Keys(d) if isinstance(keys, list): if len(keys) > 0: new_edge = Topology.SetDictionary(new_edge, d) new_edges.append(new_edge) for adj_edge_ev in adj_edges_ev: ov, flag = OppositeVertex(adj_edge_ev, ev) if flag == 0: new_edge = Edge.ByVertices([ov, vertex]) else: new_edge = Edge.ByVertices([vertex, ov]) d = Topology.Dictionary(adj_edge_ev) keys = Dictionary.Keys(d) if isinstance(keys, list): if len(keys) > 0: new_edge = Topology.SetDictionary(new_edge, d) new_edges.append(new_edge) for new_edge in new_edges: graph = Graph.AddEdge(graph, new_edge, transferVertexDictionaries=True, transferEdgeDictionaries=True, tolerance=tolerance) graph = Graph.RemoveVertex(graph,sv, tolerance=tolerance) graph = Graph.RemoveVertex(graph,ev, tolerance=tolerance) return graph
def DegreeSequence(graph)
-
Returns the degree sequence of the input graph. See https://mathworld.wolfram.com/DegreeSequence.html.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
list
- The degree sequence of the input graph.
Expand source code
@staticmethod def DegreeSequence(graph): """ Returns the degree sequence of the input graph. See https://mathworld.wolfram.com/DegreeSequence.html. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- list The degree sequence of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.DegreeSequence - Error: The input graph is not a valid graph. Returning None.") return None sequence = [] _ = graph.DegreeSequence(sequence) return sequence
def Density(graph)
-
Returns the density of the input graph. See https://en.wikipedia.org/wiki/Dense_graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
float
- The density of the input graph.
Expand source code
@staticmethod def Density(graph): """ Returns the density of the input graph. See https://en.wikipedia.org/wiki/Dense_graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- float The density of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Density - Error: The input graph is not a valid graph. Returning None.") return None return graph.Density()
def DepthMap(graph, vertices=None, tolerance=0.0001)
-
Return the depth map of the input list of vertices within the input graph. The returned list contains the total of the topological distances of each vertex to every other vertex in the input graph. The order of the depth map list is the same as the order of the input list of vertices. If no vertices are specified, the depth map of all the vertices in the input graph is computed.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertices
:list
, optional- The input list of vertices. The default is None.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The depth map of the input list of vertices within the input graph.
Expand source code
@staticmethod def DepthMap(graph, vertices=None, tolerance=0.0001): """ Return the depth map of the input list of vertices within the input graph. The returned list contains the total of the topological distances of each vertex to every other vertex in the input graph. The order of the depth map list is the same as the order of the input list of vertices. If no vertices are specified, the depth map of all the vertices in the input graph is computed. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The depth map of the input list of vertices within the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.DepthMap - Error: The input graph is not a valid graph. Returning None.") return None graphVertices = Graph.Vertices(graph) if not isinstance(vertices, list): vertices = graphVertices else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.DepthMap - Error: The input list of vertices does not contain any valid vertices. Returning None.") return None depthMap = [] for va in vertices: depth = 0 for vb in graphVertices: if Topology.IsSame(va, vb): dist = 0 else: dist = Graph.TopologicalDistance(graph, va, vb, tolerance) depth = depth + dist depthMap.append(depth) return depthMap
def Diameter(graph)
-
Returns the diameter of the input graph. See https://mathworld.wolfram.com/GraphDiameter.html.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
int
- The diameter of the input graph.
Expand source code
@staticmethod def Diameter(graph): """ Returns the diameter of the input graph. See https://mathworld.wolfram.com/GraphDiameter.html. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The diameter of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Diameter - Error: The input graph is not a valid graph. Returning None.") return None return graph.Diameter()
def Dictionary(graph)
-
Returns the dictionary of the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
topologic_core.Dictionary
- The dictionary of the input graph.
Expand source code
@staticmethod def Dictionary(graph): """ Returns the dictionary of the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- topologic_core.Dictionary The dictionary of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Dictionary - Error: the input graph parameter is not a valid graph. Returning None.") return None return graph.GetDictionary()
def Distance(graph, vertexA, vertexB, tolerance=0.0001)
-
Returns the shortest-path distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory).
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
int
- The shortest-path distance between the input vertices.
Expand source code
@staticmethod def Distance(graph, vertexA, vertexB, tolerance=0.0001): """ Returns the shortest-path distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory). Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- int The shortest-path distance between the input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Distance - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.Distance - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.Distance - Error: The input vertexB is not a valid vertex. Returning None.") return None return graph.TopologicalDistance(vertexA, vertexB, tolerance)
def Edge(graph, vertexA, vertexB, tolerance=0.0001)
-
Returns the edge in the input graph that connects in the input vertices.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input Vertex.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Edge
- The edge in the input graph that connects the input vertices.
Expand source code
@staticmethod def Edge(graph, vertexA, vertexB, tolerance=0.0001): """ Returns the edge in the input graph that connects in the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input Vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Edge The edge in the input graph that connects the input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Edge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.Edge - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.Edge - Error: The input vertexB is not a valid vertex. Returning None.") return None return graph.Edge(vertexA, vertexB, tolerance)
def Edges(graph, vertices=None, tolerance=0.0001)
-
Returns the edges found in the input graph. If the input list of vertices is specified, this method returns the edges connected to this list of vertices. Otherwise, it returns all graph edges.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertices
:list
, optional- An optional list of vertices to restrict the returned list of edges only to those connected to this list.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of edges in the graph.
Expand source code
@staticmethod def Edges(graph, vertices=None, tolerance=0.0001): """ Returns the edges found in the input graph. If the input list of vertices is specified, this method returns the edges connected to this list of vertices. Otherwise, it returns all graph edges. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional An optional list of vertices to restrict the returned list of edges only to those connected to this list. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of edges in the graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Edges - Error: The input graph is not a valid graph. Returning None.") return None if not vertices: edges = [] _ = graph.Edges(edges, tolerance) return edges else: vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.Edges - Error: The input list of vertices does not contain any valid vertices. Returning None.") return None edges = [] _ = graph.Edges(vertices, tolerance, edges) return list(dict.fromkeys(edges)) # remove duplicates
def ExportToAdjacencyMatrixCSV(adjacencyMatrix, path)
-
Exports the input graph into a set of CSV files compatible with DGL.
Parameters
path
:str
- The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved.
Returns
bool
- True if the graph has been successfully exported. False otherwise.
Expand source code
@staticmethod def ExportToAdjacencyMatrixCSV(adjacencyMatrix, path): """ Exports the input graph into a set of CSV files compatible with DGL. Parameters ---------- path : str The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved. Returns ------- bool True if the graph has been successfully exported. False otherwise. """ # Convert the adjacency matrix (nested list) to a DataFrame adjacency_matrix_df = pd.DataFrame(adjacencyMatrix) # Export the DataFrame to a CSV file try: adjacency_matrix_df.to_csv(path, index=False, header=False) return True except: return False
def ExportToBOT(graph, path, format='turtle', overwrite=False, bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel='Site_0001', siteDictionary=None, buildingLabel='Building_0001', buildingDictionary=None, storeyPrefix='Storey', floorLevels=[], labelKey='label', typeKey='type', geometryKey='brep', spaceType='space', wallType='wall', slabType='slab', doorType='door', windowType='window', contentType='content')
-
Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/.
Parameters
graph
:topologic_core.Graph
- The input graph.
format
:str
, optional- The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads
path
:str
- The desired path to where the RDF/BOT file will be saved.
overwrite
:bool
, optional- If set to True, any existing file is overwritten. Otherwise, it is not. The default is False.
bidirectional
:bool
, optional- If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False.
includeAttributes
:bool
, optional- If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False.
includeLabel
:bool
, optional- If set to True, a label is attached to each node. Otherwise, it is not. The default is False.
includeGeometry
:bool
, optional- If set to True, the geometry associated with vertices in the graph are written out. Otherwise, it is not. The default is False.
siteLabel
:str
, optional- The desired site label. The default is "Site_0001".
siteDictionary
:dict
, optional- The dictionary of site attributes to include in the output. The default is None.
buildingLabel
:str
, optional- The desired building label. The default is "Building_0001".
buildingDictionary
:dict
, optional- The dictionary of building attributes to include in the output. The default is None.
storeyPrefix
:str
, optional- The desired prefixed to use for each building storey. The default is "Storey".
floorLevels
:list
, optional- The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the nodes' 'z' attribute.
typeKey
:str
, optional- The dictionary key to use to look up the type of the node. The default is "type".
labelKey
:str
, optional- The dictionary key to use to look up the label of the node. The default is "label".
geometryKey
:str
, optional- The dictionary key to use to look up the label of the node. The default is "brep".
spaceType
:str
, optional- The dictionary string value to use to look up nodes of type "space". The default is "space".
wallType
:str
, optional- The dictionary string value to use to look up nodes of type "wall". The default is "wall".
slabType
:str
, optional- The dictionary string value to use to look up nodes of type "slab". The default is "slab".
doorType
:str
, optional- The dictionary string value to use to look up nodes of type "door". The default is "door".
windowType
:str
, optional- The dictionary string value to use to look up nodes of type "window". The default is "window".
contentType
:str
, optional- The dictionary string value to use to look up nodes of type "content". The default is "contents".
format
:str
, optional- The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads
Returns
str
- The rdf graph serialized string using the BOT ontology.
Expand source code
@staticmethod def ExportToBOT(graph, path, format="turtle", overwrite = False, bidirectional=False, includeAttributes=False, includeLabel=False, includeGeometry=False, siteLabel = "Site_0001", siteDictionary = None, buildingLabel = "Building_0001", buildingDictionary = None , storeyPrefix = "Storey", floorLevels =[], labelKey="label", typeKey="type", geometryKey="brep", spaceType = "space", wallType = "wall", slabType = "slab", doorType = "door", windowType = "window", contentType = "content", ): """ Returns an RDF graph serialized string according to the BOT ontology. See https://w3c-lbd-cg.github.io/bot/. Parameters ---------- graph : topologic_core.Graph The input graph. format : str , optional The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads path : str The desired path to where the RDF/BOT file will be saved. overwrite : bool , optional If set to True, any existing file is overwritten. Otherwise, it is not. The default is False. bidirectional : bool , optional If set to True, reverse relationships are created wherever possible. Otherwise, they are not. The default is False. includeAttributes : bool , optional If set to True, the attributes associated with vertices in the graph are written out. Otherwise, they are not. The default is False. includeLabel : bool , optional If set to True, a label is attached to each node. Otherwise, it is not. The default is False. includeGeometry : bool , optional If set to True, the geometry associated with vertices in the graph are written out. Otherwise, it is not. The default is False. siteLabel : str , optional The desired site label. The default is "Site_0001". siteDictionary : dict , optional The dictionary of site attributes to include in the output. The default is None. buildingLabel : str , optional The desired building label. The default is "Building_0001". buildingDictionary : dict , optional The dictionary of building attributes to include in the output. The default is None. storeyPrefix : str , optional The desired prefixed to use for each building storey. The default is "Storey". floorLevels : list , optional The list of floor levels. This should be a numeric list, sorted from lowest to highest. If not provided, floorLevels will be computed automatically based on the nodes' 'z' attribute. typeKey : str , optional The dictionary key to use to look up the type of the node. The default is "type". labelKey : str , optional The dictionary key to use to look up the label of the node. The default is "label". geometryKey : str , optional The dictionary key to use to look up the label of the node. The default is "brep". spaceType : str , optional The dictionary string value to use to look up nodes of type "space". The default is "space". wallType : str , optional The dictionary string value to use to look up nodes of type "wall". The default is "wall". slabType : str , optional The dictionary string value to use to look up nodes of type "slab". The default is "slab". doorType : str , optional The dictionary string value to use to look up nodes of type "door". The default is "door". windowType : str , optional The dictionary string value to use to look up nodes of type "window". The default is "window". contentType : str , optional The dictionary string value to use to look up nodes of type "content". The default is "contents". format : str , optional The desired output format, the options are listed below. Thde default is "turtle". turtle, ttl or turtle2 : Turtle, turtle2 is just turtle with more spacing & linebreaks xml or pretty-xml : RDF/XML, Was the default format, rdflib < 6.0.0 json-ld : JSON-LD , There are further options for compact syntax and other JSON-LD variants ntriples, nt or nt11 : N-Triples , nt11 is exactly like nt, only utf8 encoded n3 : Notation-3 , N3 is a superset of Turtle that also caters for rules and a few other things trig : Trig , Turtle-like format for RDF triples + context (RDF quads) and thus multiple graphs trix : Trix , RDF/XML-like format for RDF quads nquads : N-Quads , N-Triples-like format for RDF quads Returns ------- str The rdf graph serialized string using the BOT ontology. """ from os.path import exists bot_graph = Graph.BOTGraph(graph, bidirectional=bidirectional, includeAttributes=includeAttributes, includeLabel=includeLabel, includeGeometry=includeGeometry, siteLabel=siteLabel, siteDictionary=siteDictionary, buildingLabel=buildingLabel, buildingDictionary=buildingDictionary, storeyPrefix=storeyPrefix, floorLevels=floorLevels, labelKey=labelKey, typeKey=typeKey, geometryKey=geometryKey, spaceType = spaceType, wallType = wallType, slabType = slabType, doorType = doorType, windowType = windowType, contentType = contentType ) if "turtle" in format.lower() or "ttl" in format.lower() or "turtle2" in format.lower(): ext = ".ttl" elif "xml" in format.lower() or "pretty=xml" in format.lower() or "rdf/xml" in format.lower(): ext = ".xml" elif "json" in format.lower(): ext = ".json" elif "ntriples" in format.lower() or "nt" in format.lower() or "nt11" in format.lower(): ext = ".nt" elif "n3" in format.lower() or "notation" in format.lower(): ext = ".n3" elif "trig" in format.lower(): ext = ".trig" elif "trix" in format.lower(): ext = ".trix" elif "nquads" in format.lower(): ext = ".nquads" else: format = "turtle" ext = ".ttl" n = len(ext) # Make sure the file extension is .brep ext = path[len(path)-n:len(path)] if ext.lower() != ext: path = path+ext if not overwrite and exists(path): print("Graph.ExportToBOT - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None status = False try: bot_graph.serialize(destination=path, format=format) status = True except: status = False return status
def ExportToCSV(graph, path, graphLabel, graphFeatures='', graphIDHeader='graph_id', graphLabelHeader='label', graphFeaturesHeader='feat', edgeLabelKey='label', defaultEdgeLabel=0, edgeFeaturesKeys=[], edgeSRCHeader='src_id', edgeDSTHeader='dst_id', edgeLabelHeader='label', edgeFeaturesHeader='feat', edgeTrainMaskHeader='train_mask', edgeValidateMaskHeader='val_mask', edgeTestMaskHeader='test_mask', edgeMaskKey=None, edgeTrainRatio=0.8, edgeValidateRatio=0.1, edgeTestRatio=0.1, bidirectional=True, nodeLabelKey='label', defaultNodeLabel=0, nodeFeaturesKeys=[], nodeIDHeader='node_id', nodeLabelHeader='label', nodeFeaturesHeader='feat', nodeTrainMaskHeader='train_mask', nodeValidateMaskHeader='val_mask', nodeTestMaskHeader='test_mask', nodeMaskKey=None, nodeTrainRatio=0.8, nodeValidateRatio=0.1, nodeTestRatio=0.1, mantissa=6, overwrite=False)
-
Exports the input graph into a set of CSV files compatible with DGL.
Parameters
graph
:topologic_core.Graph
- The input graph
path
:str
- The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved.
graphLabel
:float
orint
- The input graph label. This can be an int (categorical) or a float (continous)
graphFeatures
:str
, optional- The input graph features. This is a single string of numeric features separated by commas. Example: "3.456, 2.011, 56.4". The defauly is "".
graphIDHeader
:str
, optional- The desired graph ID column header. The default is "graph_id".
graphLabelHeader
:str
, optional- The desired graph label column header. The default is "label".
graphFeaturesHeader
:str
, optional- The desired graph features column header. The default is "feat".
edgeLabelKey
:str
, optional- The edge label dictionary key saved in each graph edge. The default is "label".
defaultEdgeLabel
:int
, optional- The default edge label to use if no edge label is found. The default is 0.
edgeSRCHeader
:str
, optional- The desired edge source column header. The default is "src_id".
edgeDSTHeader
:str
, optional- The desired edge destination column header. The default is "dst_id".
edgeFeaturesHeader
:str
, optional- The desired edge features column header. The default is "feat".
edgeFeaturesKeys
:list
, optional- The list of feature dictionary keys saved in the dicitonaries of edges. The default is [].
edgeTrainMaskHeader
:str
, optional- The desired edge train mask column header. The default is "train_mask".
edgeValidateMaskHeader
:str
, optional- The desired edge validate mask column header. The default is "val_mask".
edgeTestMaskHeader
:str
, optional- The desired edge test mask column header. The default is "test_mask".
edgeMaskKey
:str
, optional- The dictionary key where the edge train, validate, test category is to be found. The value should be 0 for train 1 for validate, and 2 for test. If no key is found, the ratio of train/validate/test will be used. The default is "mask".
edgeTrainRatio
:float
, optional- The desired ratio of the edge data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training. This value is ignored if an edgeMaskKey is foud.
edgeValidateRatio
:float
, optional- The desired ratio of the edge data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation. This value is ignored if an edgeMaskKey is foud.
edgeTestRatio
:float
, optional- The desired ratio of the edge data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing. This value is ignored if an edgeMaskKey is foud.
bidirectional
:bool
, optional- If set to True, a reversed edge will also be saved for each edge in the graph. Otherwise, it will not. The default is True.
nodeFeaturesKeys
:list
, optional- The list of features keys saved in the dicitonaries of nodes. The default is [].
nodeLabelKey
:str
, optional- The node label dictionary key saved in each graph vertex. The default is "label".
defaultNodeLabel
:int
, optional- The default node label to use if no node label is found. The default is 0.
nodeIDHeader
:str
, optional- The desired node ID column header. The default is "node_id".
nodeLabelHeader
:str
, optional- The desired node label column header. The default is "label".
nodeFeaturesHeader
:str
, optional- The desired node features column header. The default is "feat".
nodeTrainMaskHeader
:str
, optional- The desired node train mask column header. The default is "train_mask".
nodeValidateMaskHeader
:str
, optional- The desired node validate mask column header. The default is "val_mask".
nodeTestMaskHeader
:str
, optional- The desired node test mask column header. The default is "test_mask".
nodeTrainRatio
:float
, optional- The desired ratio of the node data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training. This value is ignored if an nodeMaskKey is foud.
nodeValidateRatio
:float
, optional- The desired ratio of the node data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation. This value is ignored if an nodeMaskKey is foud.
nodeTestRatio
:float
, optional- The desired ratio of the node data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing. This value is ignored if an nodeMaskKey is foud.
mantissa
:int
, optional- The desired length of the mantissa. The default is 6.
overwrite
:bool
, optional- If set to True, any existing files are overwritten. Otherwise, the input list of graphs is appended to the end of each file. The default is False.
Returns
bool
- True if the graph has been successfully exported. False otherwise.
Expand source code
@staticmethod def ExportToCSV(graph, path, graphLabel, graphFeatures="", graphIDHeader="graph_id", graphLabelHeader="label", graphFeaturesHeader="feat", edgeLabelKey="label", defaultEdgeLabel=0, edgeFeaturesKeys=[], edgeSRCHeader="src_id", edgeDSTHeader="dst_id", edgeLabelHeader="label", edgeFeaturesHeader="feat", edgeTrainMaskHeader="train_mask", edgeValidateMaskHeader="val_mask", edgeTestMaskHeader="test_mask", edgeMaskKey=None, edgeTrainRatio=0.8, edgeValidateRatio=0.1, edgeTestRatio=0.1, bidirectional=True, nodeLabelKey="label", defaultNodeLabel=0, nodeFeaturesKeys=[], nodeIDHeader="node_id", nodeLabelHeader="label", nodeFeaturesHeader="feat", nodeTrainMaskHeader="train_mask", nodeValidateMaskHeader="val_mask", nodeTestMaskHeader="test_mask", nodeMaskKey=None, nodeTrainRatio=0.8, nodeValidateRatio=0.1, nodeTestRatio=0.1, mantissa=6, overwrite=False): """ Exports the input graph into a set of CSV files compatible with DGL. Parameters ---------- graph : topologic_core.Graph The input graph path : str The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved. graphLabel : float or int The input graph label. This can be an int (categorical) or a float (continous) graphFeatures : str , optional The input graph features. This is a single string of numeric features separated by commas. Example: "3.456, 2.011, 56.4". The defauly is "". graphIDHeader : str , optional The desired graph ID column header. The default is "graph_id". graphLabelHeader : str , optional The desired graph label column header. The default is "label". graphFeaturesHeader : str , optional The desired graph features column header. The default is "feat". edgeLabelKey : str , optional The edge label dictionary key saved in each graph edge. The default is "label". defaultEdgeLabel : int , optional The default edge label to use if no edge label is found. The default is 0. edgeSRCHeader : str , optional The desired edge source column header. The default is "src_id". edgeDSTHeader : str , optional The desired edge destination column header. The default is "dst_id". edgeFeaturesHeader : str , optional The desired edge features column header. The default is "feat". edgeFeaturesKeys : list , optional The list of feature dictionary keys saved in the dicitonaries of edges. The default is []. edgeTrainMaskHeader : str , optional The desired edge train mask column header. The default is "train_mask". edgeValidateMaskHeader : str , optional The desired edge validate mask column header. The default is "val_mask". edgeTestMaskHeader : str , optional The desired edge test mask column header. The default is "test_mask". edgeMaskKey : str , optional The dictionary key where the edge train, validate, test category is to be found. The value should be 0 for train 1 for validate, and 2 for test. If no key is found, the ratio of train/validate/test will be used. The default is "mask". edgeTrainRatio : float , optional The desired ratio of the edge data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training. This value is ignored if an edgeMaskKey is foud. edgeValidateRatio : float , optional The desired ratio of the edge data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation. This value is ignored if an edgeMaskKey is foud. edgeTestRatio : float , optional The desired ratio of the edge data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing. This value is ignored if an edgeMaskKey is foud. bidirectional : bool , optional If set to True, a reversed edge will also be saved for each edge in the graph. Otherwise, it will not. The default is True. nodeFeaturesKeys : list , optional The list of features keys saved in the dicitonaries of nodes. The default is []. nodeLabelKey : str , optional The node label dictionary key saved in each graph vertex. The default is "label". defaultNodeLabel : int , optional The default node label to use if no node label is found. The default is 0. nodeIDHeader : str , optional The desired node ID column header. The default is "node_id". nodeLabelHeader : str , optional The desired node label column header. The default is "label". nodeFeaturesHeader : str , optional The desired node features column header. The default is "feat". nodeTrainMaskHeader : str , optional The desired node train mask column header. The default is "train_mask". nodeValidateMaskHeader : str , optional The desired node validate mask column header. The default is "val_mask". nodeTestMaskHeader : str , optional The desired node test mask column header. The default is "test_mask". nodeTrainRatio : float , optional The desired ratio of the node data to use for training. The number must be between 0 and 1. The default is 0.8 which means 80% of the data will be used for training. This value is ignored if an nodeMaskKey is foud. nodeValidateRatio : float , optional The desired ratio of the node data to use for validation. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for validation. This value is ignored if an nodeMaskKey is foud. nodeTestRatio : float , optional The desired ratio of the node data to use for testing. The number must be between 0 and 1. The default is 0.1 which means 10% of the data will be used for testing. This value is ignored if an nodeMaskKey is foud. mantissa : int , optional The desired length of the mantissa. The default is 6. overwrite : bool , optional If set to True, any existing files are overwritten. Otherwise, the input list of graphs is appended to the end of each file. The default is False. Returns ------- bool True if the graph has been successfully exported. False otherwise. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology import os import math import random from os.path import exists if not Topology.IsInstance(graph, "Graph"): print("Graph.ExportToCSV - Error: The input graph parameter is not a valid topologic graph. Returning None.") return None if not exists(path): try: os.mkdir(path) except: print("Graph.ExportToCSV - Error: Could not create a folder at the specified path parameter. Returning None.") return None if overwrite == False: if not exists(os.path.join(path, "graphs.csv")): print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.") return None if not exists(os.path.join(path, "edges.csv")): print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.") return None if not exists(os.path.join(path, "nodes.csv")): print("Graph.ExportToCSV - Error: Overwrite is set to False, but could not find a graphs.csv file at specified path parameter. Returning None.") return None if abs(nodeTrainRatio + nodeValidateRatio + nodeTestRatio - 1) > 0.001: print("Graph.ExportToCSV - Error: The node train, validate, test ratios do not add up to 1. Returning None") return None if abs(edgeTrainRatio + edgeValidateRatio + edgeTestRatio - 1) > 0.001: print("Graph.ExportToCSV - Error: The edge train, validate, test ratios do not add up to 1. Returning None") return None # Step 1: Export Graphs if overwrite == False: graphs = pd.read_csv(os.path.join(path,"graphs.csv")) max_id = max(list(graphs[graphIDHeader])) graph_id = max_id + 1 else: graph_id = 0 data = [[graph_id], [graphLabel], [graphFeatures]] columns = [graphIDHeader, graphLabelHeader, graphFeaturesHeader] # Write Graph Data to CSV file data = Helper.Iterate(data) data = Helper.Transpose(data) df = pd.DataFrame(data, columns=columns) if overwrite == False: df.to_csv(os.path.join(path, "graphs.csv"), mode='a', index = False, header=False) else: df.to_csv(os.path.join(path, "graphs.csv"), mode='w+', index = False, header=True) # Step 2: Export Nodes vertices = Graph.Vertices(graph) if len(vertices) < 3: print("Graph.ExportToCSV - Error: The graph is too small to be used. Returning None") return None # Shuffle the vertices vertices = random.sample(vertices, len(vertices)) node_train_max = math.floor(float(len(vertices))*nodeTrainRatio) if node_train_max == 0: node_train_max = 1 node_validate_max = math.floor(float(len(vertices))*nodeValidateRatio) if node_validate_max == 0: node_validate_max = 1 node_test_max = len(vertices) - node_train_max - node_validate_max if node_test_max == 0: node_test_max = 1 node_data = [] node_columns = [graphIDHeader, nodeIDHeader, nodeLabelHeader, nodeTrainMaskHeader, nodeValidateMaskHeader, nodeTestMaskHeader, nodeFeaturesHeader, "X", "Y", "Z"] train = 0 test = 0 validate = 0 for i, v in enumerate(vertices): # Get the node label nd = Topology.Dictionary(v) vLabel = Dictionary.ValueAtKey(nd, nodeLabelKey) if vLabel == None: vLabel = defaultNodeLabel # Get the train/validate/test mask value flag = False if not nodeMaskKey == None: if not nd == None: keys = Dictionary.Keys(nd) else: keys = [] flag = True if nodeMaskKey in keys: value = Dictionary.ValueAtKey(nd, nodeMaskKey) if not value in [0, 1, 2]: flag = True elif value == 0: train_mask = True validate_mask = False test_mask = False train = train + 1 elif value == 1: train_mask = False validate_mask = True test_mask = False validate = validate + 1 else: train_mask = False validate_mask = False test_mask = True test = test + 1 else: flag = True else: flag = True if flag: if train < node_train_max: train_mask = True validate_mask = False test_mask = False train = train + 1 elif validate < node_validate_max: train_mask = False validate_mask = True test_mask = False validate = validate + 1 elif test < node_test_max: train_mask = False validate_mask = False test_mask = True test = test + 1 else: train_mask = True validate_mask = False test_mask = False train = train + 1 # Get the features of the vertex node_features = "" node_features_keys = Helper.Flatten(nodeFeaturesKeys) for node_feature_key in node_features_keys: if len(node_features) > 0: node_features = node_features + ","+ str(round(float(Dictionary.ValueAtKey(nd, node_feature_key)),mantissa)) else: node_features = str(round(float(Dictionary.ValueAtKey(nd, node_feature_key)),mantissa)) single_node_data = [graph_id, i, vLabel, train_mask, validate_mask, test_mask, node_features, round(float(Vertex.X(v)),mantissa), round(float(Vertex.Y(v)),mantissa), round(float(Vertex.Z(v)),mantissa)] node_data.append(single_node_data) # Write Node Data to CSV file df = pd.DataFrame(node_data, columns= node_columns) if graph_id == 0: df.to_csv(os.path.join(path, "nodes.csv"), mode='w+', index = False, header=True) else: df.to_csv(os.path.join(path, "nodes.csv"), mode='a', index = False, header=False) # Step 3: Export Edges edge_data = [] edge_columns = [graphIDHeader, edgeSRCHeader, edgeDSTHeader, edgeLabelHeader, edgeTrainMaskHeader, edgeValidateMaskHeader, edgeTestMaskHeader, edgeFeaturesHeader] train = 0 test = 0 validate = 0 edges = Graph.Edges(graph) edge_train_max = math.floor(float(len(edges))*edgeTrainRatio) edge_validate_max = math.floor(float(len(edges))*edgeValidateRatio) edge_test_max = len(edges) - edge_train_max - edge_validate_max for edge in edges: # Get the edge label ed = Topology.Dictionary(edge) edge_label = Dictionary.ValueAtKey(ed, edgeLabelKey) if edge_label == None: edge_label = defaultEdgeLabel # Get the train/validate/test mask value flag = False if not edgeMaskKey == None: if not ed == None: keys = Dictionary.Keys(ed) else: keys = [] flag = True if edgeMaskKey in keys: value = Dictionary.ValueAtKey(ed, edgeMaskKey) if not value in [0, 1, 2]: flag = True elif value == 0: train_mask = True validate_mask = False test_mask = False train = train + 1 elif value == 1: train_mask = False validate_mask = True test_mask = False validate = validate + 1 else: train_mask = False validate_mask = False test_mask = True test = test + 1 else: flag = True else: flag = True if flag: if train < edge_train_max: train_mask = True validate_mask = False test_mask = False train = train + 1 elif validate < edge_validate_max: train_mask = False validate_mask = True test_mask = False validate = validate + 1 elif test < edge_test_max: train_mask = False validate_mask = False test_mask = True test = test + 1 else: train_mask = True validate_mask = False test_mask = False train = train + 1 # Get the edge features edge_features = "" edge_features_keys = Helper.Flatten(edgeFeaturesKeys) for edge_feature_key in edge_features_keys: if len(edge_features) > 0: edge_features = edge_features + ","+ str(round(float(Dictionary.ValueAtKey(ed, edge_feature_key)),mantissa)) else: edge_features = str(round(float(Dictionary.ValueAtKey(ed, edge_feature_key)), mantissa)) # Get the Source and Destination vertex indices src = Vertex.Index(Edge.StartVertex(edge), vertices) dst = Vertex.Index(Edge.EndVertex(edge), vertices) single_edge_data = [graph_id, src, dst, edge_label, train_mask, validate_mask, test_mask, edge_features] edge_data.append(single_edge_data) if bidirectional == True: single_edge_data = [graph_id, src, dst, edge_label, train_mask, validate_mask, test_mask, edge_features] edge_data.append(single_edge_data) df = pd.DataFrame(edge_data, columns=edge_columns) if graph_id == 0: df.to_csv(os.path.join(path, "edges.csv"), mode='w+', index = False, header=True) else: df.to_csv(os.path.join(path, "edges.csv"), mode='a', index = False, header=False) # Write out the meta.yaml file yaml_file = open(os.path.join(path,"meta.yaml"), "w") if graph_id > 0: yaml_file.write('dataset_name: topologic_dataset\nedge_data:\n- file_name: edges.csv\nnode_data:\n- file_name: nodes.csv\ngraph_data:\n file_name: graphs.csv') else: yaml_file.write('dataset_name: topologic_dataset\nedge_data:\n- file_name: edges.csv\nnode_data:\n- file_name: nodes.csv') yaml_file.close() return True
def ExportToGEXF(graph, path, graphWidth=20, graphLength=20, graphHeight=20, defaultVertexColor='black', defaultVertexSize=3, vertexLabelKey=None, vertexColorKey=None, vertexSizeKey=None, defaultEdgeColor='black', defaultEdgeWeight=1, defaultEdgeType='undirected', edgeLabelKey=None, edgeColorKey=None, edgeWeightKey=None, overwrite=False)
-
Exports the input graph to a Graph Exchange XML (GEXF) file format. See https://gexf.net/
Parameters
graph
:topologic_core.Graph
- The input graph
path
:str
- The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved.
graphWidth
:float
orint
, optional- The desired graph width. The default is 20.
graphLength
:float
orint
, optional- The desired graph length. The default is 20.
graphHeight
:float
orint
, optional- The desired graph height. The default is 20.
defaultVertexColor
:str
, optional- The desired default vertex color. The default is "black".
defaultVertexSize
:float
orint
, optional- The desired default vertex size. The default is 3.
defaultEdgeColor
:str
, optional- The desired default edge color. The default is "black".
defaultEdgeWeight
:float
orint
, optional- The desired default edge weight. The edge weight determines the width of the displayed edge. The default is 3.
defaultEdgeType
:str
, optional- The desired default edge type. This can be one of "directed" or "undirected". The default is "undirected".
vertexLabelKey
:str
, optional- If specified, the vertex dictionary is searched for this key to determine the vertex label. If not specified the vertex label being is set to "Node X" where is X is a unique number. The default is None.
vertexColorKey
:str
, optional- If specified, the vertex dictionary is searched for this key to determine the vertex color. If not specified the vertex color is set to the value defined by defaultVertexColor parameter. The default is None.
vertexSizeKey
:str
, optional- If specified, the vertex dictionary is searched for this key to determine the vertex size. If not specified the vertex size is set to the value defined by defaultVertexSize parameter. The default is None.
edgeLabelKey
:str
, optional- If specified, the edge dictionary is searched for this key to determine the edge label. If not specified the edge label being is set to "Edge X" where is X is a unique number. The default is None.
edgeColorKey
:str
, optional- If specified, the edge dictionary is searched for this key to determine the edge color. If not specified the edge color is set to the value defined by defaultEdgeColor parameter. The default is None.
edgeWeightKey
:str
, optional- If specified, the edge dictionary is searched for this key to determine the edge weight. If not specified the edge weight is set to the value defined by defaultEdgeWeight parameter. The default is None.
overwrite
:bool
, optional- If set to True, any existing file is overwritten. Otherwise, it is not. The default is False.
Returns
bool
- True if the graph has been successfully exported. False otherwise.
Expand source code
@staticmethod def ExportToGEXF(graph, path, graphWidth=20, graphLength=20, graphHeight=20, defaultVertexColor="black", defaultVertexSize=3, vertexLabelKey=None, vertexColorKey=None, vertexSizeKey=None, defaultEdgeColor="black", defaultEdgeWeight=1, defaultEdgeType="undirected", edgeLabelKey=None, edgeColorKey=None, edgeWeightKey=None, overwrite=False): """ Exports the input graph to a Graph Exchange XML (GEXF) file format. See https://gexf.net/ Parameters ---------- graph : topologic_core.Graph The input graph path : str The desired path to the output folder where the graphs, edges, and nodes CSV files will be saved. graphWidth : float or int , optional The desired graph width. The default is 20. graphLength : float or int , optional The desired graph length. The default is 20. graphHeight : float or int , optional The desired graph height. The default is 20. defaultVertexColor : str , optional The desired default vertex color. The default is "black". defaultVertexSize : float or int , optional The desired default vertex size. The default is 3. defaultEdgeColor : str , optional The desired default edge color. The default is "black". defaultEdgeWeight : float or int , optional The desired default edge weight. The edge weight determines the width of the displayed edge. The default is 3. defaultEdgeType : str , optional The desired default edge type. This can be one of "directed" or "undirected". The default is "undirected". vertexLabelKey : str , optional If specified, the vertex dictionary is searched for this key to determine the vertex label. If not specified the vertex label being is set to "Node X" where is X is a unique number. The default is None. vertexColorKey : str , optional If specified, the vertex dictionary is searched for this key to determine the vertex color. If not specified the vertex color is set to the value defined by defaultVertexColor parameter. The default is None. vertexSizeKey : str , optional If specified, the vertex dictionary is searched for this key to determine the vertex size. If not specified the vertex size is set to the value defined by defaultVertexSize parameter. The default is None. edgeLabelKey : str , optional If specified, the edge dictionary is searched for this key to determine the edge label. If not specified the edge label being is set to "Edge X" where is X is a unique number. The default is None. edgeColorKey : str , optional If specified, the edge dictionary is searched for this key to determine the edge color. If not specified the edge color is set to the value defined by defaultEdgeColor parameter. The default is None. edgeWeightKey : str , optional If specified, the edge dictionary is searched for this key to determine the edge weight. If not specified the edge weight is set to the value defined by defaultEdgeWeight parameter. The default is None. overwrite : bool , optional If set to True, any existing file is overwritten. Otherwise, it is not. The default is False. Returns ------- bool True if the graph has been successfully exported. False otherwise. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Color import Color import numbers from datetime import datetime import os from os.path import exists def create_gexf_file(nodes, edges, default_edge_type, node_attributes, edge_attributes, path): with open(path, 'w') as file: # Write the GEXF header formatted_date = datetime.now().strftime("%Y-%m-%d") if not isinstance(default_edge_type, str): default_edge_type = "undirected" if default_edge_type.lower() == "directed": defaultedge_type = "directed" else: default_edge_type = "undirected" file.write('<?xml version="1.0" encoding="UTF-8"?>\n') file.write('<gexf version="1.3" xmlns="http://www.gephi.org/gexf" xmlns:viz="http://www.gephi.org/gexf/viz">\n') file.write(f'<meta lastmodifieddate="{formatted_date}">\n') file.write('<creator>Topologic GEXF Generator</creator>\n') file.write('<title>"Topologic Graph"</title>\n') file.write('<description>"This is a Topologic Graph"</description>\n') file.write('</meta>\n') file.write(f'<graph type="static" defaultedgetype="{defaultEdgeType}">\n') # Write attribute definitions file.write('<attributes class="node" mode="static">\n') for attr_name, attr_type in node_attributes.items(): file.write(f'<attribute id="{attr_name}" title="{attr_name}" type="{attr_type}"/>\n') file.write('</attributes>\n') # Write attribute definitions file.write('<attributes class="edge" mode="static">\n') for attr_name, attr_type in edge_attributes.items(): file.write(f'<attribute id="{attr_name}" title="{attr_name}" type="{attr_type}"/>\n') file.write('</attributes>\n') # Write nodes with attributes file.write('<nodes>\n') for node_id, node_attrs in nodes.items(): file.write(f'<node id="{node_id}" label="{node_attrs["label"]}">\n') if "r" in node_attrs and "g" in node_attrs and "b" in node_attrs: r = node_attrs['r'] g = node_attrs['g'] b = node_attrs['b'] file.write(f'<viz:color r="{r}" g="{g}" b="{b}"/>\n') if "size" in node_attrs: file.write(f'<viz:size value="{node_attrs["size"]}"/>\n') if "x" in node_attrs and "y" in node_attrs and "z" in node_attrs: file.write(f'<viz:position x="{node_attrs["x"]}" y="{node_attrs["y"]}" z="{node_attrs["z"]}"/>\n') file.write('<attvalues>\n') keys = node_attrs.keys() for key in keys: file.write(f'<attvalue id="{key}" value="{node_attrs[key]}"/>\n') file.write('</attvalues>\n') file.write('</node>\n') file.write('</nodes>\n') # Write edges with attributes file.write('<edges>\n') for edge_id, edge_attrs in edges.items(): source, target = edge_id file.write(f'<edge id="{edge_id}" source="{source}" target="{target}" label="{edge_attrs["label"]}">\n') if "color" in edge_attrs: r, g, b = Color.ByCSSNamedColor(edge_attrs["color"]) file.write(f'<viz:color r="{r}" g="{g}" b="{b}"/>\n') file.write('<attvalues>\n') keys = edge_attrs.keys() for key in keys: file.write(f'<attvalue id="{key}" value="{edge_attrs[key]}"/>\n') file.write('</attvalues>\n') file.write('</edge>\n') file.write('</edges>\n') # Write the GEXF footer file.write('</graph>\n') file.write('</gexf>\n') def valueType(value): if isinstance(value, str): return 'string' elif isinstance(value, float): return 'double' elif isinstance(value, int): return 'integer' else: return 'string' if not Topology.IsInstance(graph, "Graph"): print("Graph.ExportToGEXF - Error: the input graph parameter is not a valid graph. Returning None.") return None if not isinstance(path, str): print("Graph.ExportToGEXF - Error: the input path parameter is not a valid string. Returning None.") return None # Make sure the file extension is .brep ext = path[len(path)-5:len(path)] if ext.lower() != ".gexf": path = path+".gexf" if not overwrite and exists(path): print("Graph.ExportToGEXF - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None g_vertices = Graph.Vertices(graph) g_edges = Graph.Edges(graph) node_attributes = {'id': 'integer', 'label': 'string', 'x': 'double', 'y': 'double', 'z': 'double', 'r': 'integer', 'g': 'integer', 'b': 'integer', 'color': 'string', 'size': 'double'} nodes = {} # Resize the graph xList = [Vertex.X(v) for v in g_vertices] yList = [Vertex.Y(v) for v in g_vertices] zList = [Vertex.Z(v) for v in g_vertices] xMin = min(xList) xMax = max(xList) yMin = min(yList) yMax = max(yList) zMin = min(zList) zMax = max(zList) width = max(abs(xMax - xMin), 0.01) length = max(abs(yMax - yMin), 0.01) height = max(abs(zMax - zMin), 0.01) x_sf = graphWidth/width y_sf = graphLength/length z_sf = graphHeight/height x_avg = sum(xList)/float(len(xList)) y_avg = sum(yList)/float(len(yList)) z_avg = sum(zList)/float(len(zList)) for i, v in enumerate(g_vertices): node_dict = {} d = Topology.Dictionary(v) keys = Dictionary.Keys(d) values = Dictionary.Values(d) x = (Vertex.X(v) - x_avg)*x_sf + x_avg y = (Vertex.Y(v) - y_avg)*y_sf + y_avg z = (Vertex.Z(v) - z_avg)*z_sf + z_avg node_dict['x'] = x node_dict['y'] = y node_dict['z'] = z node_dict['id'] = i for m, key in enumerate(keys): if key == "psets": #We cannot handle IFC psets at this point. continue if key == "id": key = "TOPOLOGIC_ID" if not key in node_attributes.keys(): node_attributes[key] = valueType(values[m]) if isinstance(values[m], str): values[m] = values[m].replace('&','&') values[m] = values[m].replace('<','<') values[m] = values[m].replace('>','>') values[m] = values[m].replace('"','"') values[m] = values[m].replace('\'',''') node_dict[key] = values[m] dict_color = None if not defaultVertexColor in Color.CSSNamedColors(): defaultVertexColor = "black" vertex_color = defaultVertexColor if isinstance(vertexColorKey, str): dict_color = Dictionary.ValueAtKey(d, vertexColorKey) if not dict_color == None: vertex_color = dict_color if not vertex_color in Color.CSSNamedColors(): vertex_color = defaultVertexColor node_dict['color'] = vertex_color r, g, b = Color.ByCSSNamedColor(vertex_color) node_dict['r'] = r node_dict['g'] = g node_dict['b'] = b dict_size = None if isinstance(vertexSizeKey, str): dict_size = Dictionary.ValueAtKey(d, vertexSizeKey) vertex_size = defaultVertexSize if not dict_size == None: if isinstance(dict_size, numbers.Real): vertex_size = dict_size if not isinstance(vertex_size, numbers.Real): vertex_size = defaultVertexSize node_dict['size'] = vertex_size vertex_label = "Node "+str(i) if isinstance(vertexLabelKey, str): vertex_label = Dictionary.ValueAtKey(d, vertexLabelKey) if not isinstance(vertex_label, str): vertex_label = "Node "+str(i) if isinstance(vertex_label, str): vertex_label = vertex_label.replace('&','&') vertex_label = vertex_label.replace('<','<') vertex_label = vertex_label.replace('>','>') vertex_label = vertex_label.replace('"','"') vertex_label = vertex_label.replace('\'',''') node_dict['label'] = vertex_label nodes[i] = node_dict edge_attributes = {'id': 'integer', 'label': 'string', 'source': 'integer', 'target': 'integer', 'r': 'integer', 'g': 'integer', 'b': 'integer', 'color': 'string', 'weight': 'double'} edges = {} for i, edge in enumerate(g_edges): edge_dict = {} d = Topology.Dictionary(edge) keys = Dictionary.Keys(d) values = Dictionary.Values(d) edge_dict['id'] = i for m, key in enumerate(keys): if key == "id": key = "TOPOLOGIC_ID" if not key in edge_attributes.keys(): edge_attributes[key] = valueType(values[m]) edge_dict[key] = values[m] dict_color = None if not defaultEdgeColor in Color.CSSNamedColors(): defaultEdgeColor = "black" edge_color = defaultEdgeColor if isinstance(edgeColorKey, str): dict_color = Dictionary.ValueAtKey(d, edgeColorKey) if not dict_color == None: edge_color = dict_color if not vertex_color in Color.CSSNamedColors(): edge_color = defaultVertexColor edge_dict['color'] = edge_color r, g, b = Color.ByCSSNamedColor(edge_color) edge_dict['r'] = r edge_dict['g'] = g edge_dict['b'] = b dict_weight = None if not isinstance(defaultEdgeWeight, numbers.Real): defaultEdgeWeight = 1 edge_weight = defaultEdgeWeight if isinstance(edgeWeightKey, str): dict_weight = Dictionary.ValueAtKey(d, edgeWeightKey) if not dict_weight == None: if isinstance(dict_weight, numbers.Real): edge_weight = dict_weight if not isinstance(edge_weight, numbers.Real): edge_weight = defaultEdgeWeight edge_dict['weight'] = edge_weight sv = g_vertices[Vertex.Index(Edge.StartVertex(edge), g_vertices)] ev = g_vertices[Vertex.Index(Edge.EndVertex(edge), g_vertices)] svid = Vertex.Index(sv, g_vertices) edge_dict['source'] = svid evid = Vertex.Index(ev, g_vertices) edge_dict['target'] = evid edge_label = "Edge "+str(svid)+"-"+str(evid) if isinstance(edgeLabelKey, str): edge_label = Dictionary.ValueAtKey(d, edgeLabelKey) if not isinstance(edge_label, str): edge_label = "Edge "+str(svid)+"-"+str(evid) edge_dict['label'] = edge_label edges[(str(svid), str(evid))] = edge_dict create_gexf_file(nodes, edges, defaultEdgeType, node_attributes, edge_attributes, path) return True
def ExportToJSON(graph, path, verticesKey='vertices', edgesKey='edges', vertexLabelKey='', edgeLabelKey='', xKey='x', yKey='y', zKey='z', indent=4, sortKeys=False, mantissa=6, overwrite=False)
-
Exports the input graph to a JSON file.
Parameters
graph
:topologic_core.Graph
- The input graph.
path
:str
- The path to the JSON file.
verticesKey
:str
, optional- The desired key name to call vertices. The default is "vertices".
edgesKey
:str
, optional- The desired key name to call edges. The default is "edges".
vertexLabelKey
:str
, optional- If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique.
edgeLabelKey
:str
, optional- If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique.
xKey
:str
, optional- The desired key name to use for x-coordinates. The default is "x".
yKey
:str
, optional- The desired key name to use for y-coordinates. The default is "y".
zKey
:str
, optional- The desired key name to use for z-coordinates. The default is "z".
indent
:int
, optional- The desired amount of indent spaces to use. The default is 4.
sortKeys
:bool
, optional- If set to True, the keys will be sorted. Otherwise, they won't be. The default is False.
mantissa
:int
, optional- The desired length of the mantissa. The default is 6.
overwrite
:bool
, optional- If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
Returns
bool
- The status of exporting the JSON file. If True, the operation was successful. Otherwise, it was unsuccesful.
Expand source code
@staticmethod def ExportToJSON(graph, path, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", xKey="x", yKey="y", zKey="z", indent=4, sortKeys=False, mantissa=6, overwrite=False): """ Exports the input graph to a JSON file. Parameters ---------- graph : topologic_core.Graph The input graph. path : str The path to the JSON file. verticesKey : str , optional The desired key name to call vertices. The default is "vertices". edgesKey : str , optional The desired key name to call edges. The default is "edges". vertexLabelKey : str , optional If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique. edgeLabelKey : str , optional If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique. xKey : str , optional The desired key name to use for x-coordinates. The default is "x". yKey : str , optional The desired key name to use for y-coordinates. The default is "y". zKey : str , optional The desired key name to use for z-coordinates. The default is "z". indent : int , optional The desired amount of indent spaces to use. The default is 4. sortKeys : bool , optional If set to True, the keys will be sorted. Otherwise, they won't be. The default is False. mantissa : int , optional The desired length of the mantissa. The default is 6. overwrite : bool , optional If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False. Returns ------- bool The status of exporting the JSON file. If True, the operation was successful. Otherwise, it was unsuccesful. """ import json from os.path import exists # Make sure the file extension is .json ext = path[len(path)-5:len(path)] if ext.lower() != ".json": path = path+".json" if not overwrite and exists(path): print("Graph.ExportToJSON - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None f = None try: if overwrite == True: f = open(path, "w") else: f = open(path, "x") # Try to create a new File except: raise Exception("Graph.ExportToJSON - Error: Could not create a new file at the following location: "+path) if (f): jsondata = Graph.JSONData(graph, verticesKey=verticesKey, edgesKey=edgesKey, vertexLabelKey=vertexLabelKey, edgeLabelKey=edgeLabelKey, xKey=xKey, yKey=yKey, zKey=zKey, mantissa=mantissa) if jsondata != None: json.dump(jsondata, f, indent=indent, sort_keys=sortKeys) f.close() return True else: f.close() return False return False
def Flatten(graph, layout='spring', k=0.8, seed=None, iterations=50, rootVertex=None, tolerance=0.0001)
-
Flattens the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
layout
:str
, optional- The desired mode for flattening. If set to 'spring', the algorithm uses a simplified version of the Fruchterman-Reingold force-directed algorithm to flatten and distribute the vertices. If set to 'radial', the nodes will be distributed along a circle. If set to 'tree', the nodes will be distributed using the Reingold-Tillford layout. The default is 'spring'.
k
:float
, optional- The desired spring constant to use for the attractive and repulsive forces. The default is 0.8.
seed
:int
, optional- The desired random seed to use. The default is None.
iterations
:int
, optional- The desired maximum number of iterations to solve the forces in the 'spring' mode. The default is 50.
rootVertex
:topologic_core.Vertex
, optional- The desired vertex to use as the root of the tree and radial layouts.
Returns
topologic_core.Graph
- The flattened graph.
Expand source code
@staticmethod def Flatten(graph, layout="spring", k=0.8, seed=None, iterations=50, rootVertex=None, tolerance=0.0001): """ Flattens the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. layout : str , optional The desired mode for flattening. If set to 'spring', the algorithm uses a simplified version of the Fruchterman-Reingold force-directed algorithm to flatten and distribute the vertices. If set to 'radial', the nodes will be distributed along a circle. If set to 'tree', the nodes will be distributed using the Reingold-Tillford layout. The default is 'spring'. k : float, optional The desired spring constant to use for the attractive and repulsive forces. The default is 0.8. seed : int , optional The desired random seed to use. The default is None. iterations : int , optional The desired maximum number of iterations to solve the forces in the 'spring' mode. The default is 50. rootVertex : topologic_core.Vertex , optional The desired vertex to use as the root of the tree and radial layouts. Returns ------- topologic_core.Graph The flattened graph. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology import numpy as np def buchheim(tree): dt = firstwalk(_DrawTree(tree)) min = second_walk(dt) if min < 0: third_walk(dt, -min) return dt def third_walk(tree, n): tree.x += n for c in tree.children: third_walk(c, n) def firstwalk(v, distance=1.0): if len(v.children) == 0: if v.lmost_sibling: v.x = v.lbrother().x + distance else: v.x = 0.0 else: default_ancestor = v.children[0] for w in v.children: firstwalk(w) default_ancestor = apportion(w, default_ancestor, distance) execute_shifts(v) midpoint = (v.children[0].x + v.children[-1].x) / 2 w = v.lbrother() if w: v.x = w.x + distance v.mod = v.x - midpoint else: v.x = midpoint return v def apportion(v, default_ancestor, distance): w = v.lbrother() if w is not None: vir = vor = v vil = w vol = v.lmost_sibling sir = sor = v.mod sil = vil.mod sol = vol.mod while vil.right() and vir.left(): vil = vil.right() vir = vir.left() vol = vol.left() vor = vor.right() vor.ancestor = v shift = (vil.x + sil) - (vir.x + sir) + distance if shift > 0: move_subtree(ancestor(vil, v, default_ancestor), v, shift) sir = sir + shift sor = sor + shift sil += vil.mod sir += vir.mod sol += vol.mod sor += vor.mod if vil.right() and not vor.right(): vor.thread = vil.right() vor.mod += sil - sor else: if vir.left() and not vol.left(): vol.thread = vir.left() vol.mod += sir - sol default_ancestor = v return default_ancestor def move_subtree(wl, wr, shift): subtrees = wr.number - wl.number wr.change -= shift / subtrees wr.shift += shift wl.change += shift / subtrees wr.x += shift wr.mod += shift def execute_shifts(v): shift = change = 0 for w in v.children[::-1]: w.x += shift w.mod += shift change += w.change shift += w.shift + change def ancestor(vil, v, default_ancestor): if vil.ancestor in v.parent.children: return vil.ancestor else: return default_ancestor def second_walk(v, m=0, depth=0, min=None): v.x += m v.y = depth if min is None or v.x < min: min = v.x for w in v.children: min = second_walk(w, m + v.mod, depth + 1, min) return min def edge_list_to_adjacency_matrix(edge_list): """Converts an edge list to an adjacency matrix. Args: edge_list: A list of tuples, where each tuple is an edge. Returns: A numpy array representing the adjacency matrix. """ # Get the number of nodes from the edge list. num_nodes = max([max(edge) for edge in edge_list]) + 1 # Create an adjacency matrix. adjacency_matrix = np.zeros((num_nodes, num_nodes)) # Fill in the adjacency matrix. for edge in edge_list: adjacency_matrix[edge[0], edge[1]] = 1 adjacency_matrix[edge[1], edge[0]] = 1 return adjacency_matrix def tree_from_edge_list(edge_list, root_index=0): adj_matrix = edge_list_to_adjacency_matrix(edge_list) num_nodes = adj_matrix.shape[0] root = _Tree(str(root_index)) is_visited = np.zeros(num_nodes) is_visited[root_index] = 1 old_roots = [root] new_roots = [] while(np.sum(is_visited) < num_nodes): new_roots = [] for temp_root in old_roots: children = [] for i in range(num_nodes): if adj_matrix[int(temp_root.node), i] == 1 and is_visited[i] == 0: is_visited[i] = 1 child = _Tree(str(i)) temp_root.children.append(child) children.append(child) new_roots.extend(children) old_roots = new_roots return root, num_nodes def spring_layout(edge_list, iterations=500, k=None, seed=None): # Compute the layout of a graph using the Fruchterman-Reingold algorithm # with a force-directed layout approach. adj_matrix = edge_list_to_adjacency_matrix(edge_list) # Set the random seed if seed is not None: np.random.seed(seed) # Set the optimal distance between nodes if k is None or k <= 0: k = np.sqrt(1.0 / adj_matrix.shape[0]) # Initialize the positions of the nodes randomly pos = np.random.rand(adj_matrix.shape[0], 2) # Compute the initial temperature t = 0.1 * np.max(pos) # Compute the cooling factor cooling_factor = t / iterations # Iterate over the specified number of iterations for i in range(iterations): # Compute the distance between each pair of nodes delta = pos[:, np.newaxis, :] - pos[np.newaxis, :, :] distance = np.linalg.norm(delta, axis=-1) # Avoid division by zero distance = np.where(distance == 0, 0.1, distance) # Compute the repulsive force between each pair of nodes repulsive_force = k ** 2 / distance ** 2 # Compute the attractive force between each pair of adjacent nodes attractive_force = adj_matrix * distance / k # Compute the total force acting on each node force = np.sum((repulsive_force - attractive_force)[:, :, np.newaxis] * delta, axis=1) # Compute the displacement of each node displacement = t * force / np.linalg.norm(force, axis=1)[:, np.newaxis] # Update the positions of the nodes pos += displacement # Cool the temperature t -= cooling_factor return pos def tree_layout(edge_list, root_index=0): root, num_nodes = tree_from_edge_list(edge_list, root_index) dt = buchheim(root) pos = np.zeros((num_nodes, 2)) pos[int(dt.tree.node), 0] = dt.x pos[int(dt.tree.node), 1] = dt.y old_roots = [dt] new_roots = [] while(len(old_roots) > 0): new_roots = [] for temp_root in old_roots: children = temp_root.children for child in children: pos[int(child.tree.node), 0] = child.x pos[int(child.tree.node), 1] = child.y new_roots.extend(children) old_roots = new_roots pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1] return pos def radial_layout(edge_list, root_index=0): root, num_nodes = tree_from_edge_list(edge_list, root_index) dt = buchheim(root) pos = np.zeros((num_nodes, 2)) pos[int(dt.tree.node), 0] = dt.x pos[int(dt.tree.node), 1] = dt.y old_roots = [dt] new_roots = [] while(len(old_roots) > 0): new_roots = [] for temp_root in old_roots: children = temp_root.children for child in children: pos[int(child.tree.node), 0] = child.x pos[int(child.tree.node), 1] = child.y new_roots.extend(children) old_roots = new_roots # pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1] pos[:, 0] = pos[:, 0] - np.min(pos[:, 0]) pos[:, 1] = pos[:, 1] - np.min(pos[:, 1]) pos[:, 0] = pos[:, 0] / np.max(pos[:, 0]) pos[:, 0] = pos[:, 0] - pos[:, 0][root_index] range_ = np.max(pos[:, 0]) - np.min(pos[:, 0]) pos[:, 0] = pos[:, 0] / range_ pos[:, 0] = pos[:, 0] * np.pi * 1.98 pos[:, 1] = pos[:, 1] / np.max(pos[:, 1]) new_pos = np.zeros((num_nodes, 2)) new_pos[:, 0] = pos[:, 1] * np.cos(pos[:, 0]) new_pos[:, 1] = pos[:, 1] * np.sin(pos[:, 0]) return new_pos def graph_layout(edge_list, layout='tree', root_index=0, k=None, seed=None, iterations=500): if layout == 'tree': return tree_layout(edge_list, root_index=root_index) elif layout == 'spring': return spring_layout(edge_list, k=k, seed=seed, iterations=iterations) elif layout == 'radial': return radial_layout(edge_list, root_index=root_index) else: raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']") def vertex_max_degree(graph, vertices): degrees = [Graph.VertexDegree(graph, vertex) for vertex in vertices] i = degrees.index(max(degrees)) return vertices[i], i if not Topology.IsInstance(graph, "Graph"): print("Graph.Flatten - Error: The input graph is not a valid topologic graph. Returning None.") return None d = Graph.MeshData(graph) vertices = d['vertices'] edges = d['edges'] v_dicts = d['vertexDictionaries'] e_dicts = d['edgeDictionaries'] vertices = Graph.Vertices(graph) if rootVertex == None: rootVertex, root_index = vertex_max_degree(graph, vertices) else: root_index = Vertex.Index(rootVertex, vertices) if 'rad' in layout.lower(): positions = radial_layout(edges, root_index=root_index) elif 'spring' in layout.lower(): positions = spring_layout(edges, k=k, seed=seed, iterations=iterations) elif 'tree' in layout.lower(): positions = tree_layout(edges, root_index=root_index) else: raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']") positions = positions.tolist() positions = [[p[0], p[1], 0] for p in positions] flat_graph = Graph.ByMeshData(positions, edges, v_dicts, e_dicts, tolerance=tolerance) return flat_graph
def GlobalClusteringCoefficient(graph)
-
Returns the global clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
int
- The computed global clustering coefficient.
Expand source code
@staticmethod def GlobalClusteringCoefficient(graph): """ Returns the global clustering coefficient of the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The computed global clustering coefficient. """ from topologicpy.Topology import Topology def global_clustering_coefficient(adjacency_matrix): total_triangles = 0 total_possible_triangles = 0 num_nodes = len(adjacency_matrix) for i in range(num_nodes): neighbors = [j for j, value in enumerate(adjacency_matrix[i]) if value == 1] num_neighbors = len(neighbors) num_triangles = 0 if num_neighbors >= 2: # Count the number of connections between the neighbors for i in range(num_neighbors): for j in range(i + 1, num_neighbors): if adjacency_matrix[neighbors[i]][neighbors[j]] == 1: num_triangles += 1 # Update total triangles and possible triangles total_triangles += num_triangles total_possible_triangles += num_neighbors * (num_neighbors - 1) // 2 # Calculate the global clustering coefficient global_clustering_coeff = 3.0 * total_triangles / total_possible_triangles if total_possible_triangles > 0 else 0.0 return global_clustering_coeff if not Topology.IsInstance(graph, "Graph"): print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.") return None adjacency_matrix = Graph.AdjacencyMatrix(graph) return global_clustering_coefficient(adjacency_matrix)
def Guid(graph)
-
Returns the guid of the input graph
Parameters
graph
:topologic_core.Graph
- The input graph.
Expand source code
@staticmethod def Guid(graph): """ Returns the guid of the input graph Parameters ---------- graph : topologic_core.Graph The input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Guid - Error: the input graph parameter is not a valid graph. Returning None.") return None return graph.GetGUID()
def IncomingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) ‑> list
-
Returns the incoming edges connected to a vertex. An edge is considered incoming if its end vertex is coincident with the input vertex.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
directed
:bool
, optional- If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of incoming edges
Expand source code
@staticmethod def IncomingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the incoming edges connected to a vertex. An edge is considered incoming if its end vertex is coincident with the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of incoming edges """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IncomingEdges - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.IncomingEdges - Error: The input vertex parameter is not a valid vertex. Returning None.") return None edges = Graph.Edges(graph, [vertex]) if directed == False: return edges incoming_edges = [] for edge in edges: ev = Edge.EndVertex(edge) if Vertex.Distance(vertex, ev) < tolerance: incoming_edges.append(edge) return incoming_edges
def IncomingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) ‑> list
-
Returns the incoming vertices connected to a vertex. A vertex is considered incoming if it is an adjacent vertex to the input vertex and the the edge connecting it to the input vertex is an incoming edge.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
directed
:bool
, optional- If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of incoming vertices
Expand source code
@staticmethod def IncomingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the incoming vertices connected to a vertex. A vertex is considered incoming if it is an adjacent vertex to the input vertex and the the edge connecting it to the input vertex is an incoming edge. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of incoming vertices """ from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IncomingVertices - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.IncomingVertices - Error: The input vertex parameter is not a valid vertex. Returning None.") return None if directed == False: return Graph.AdjacentVertices(graph, vertex) incoming_edges = Graph.IncomingEdges(graph, vertex, directed=directed, tolerance=tolerance) incoming_vertices = [] for edge in incoming_edges: sv = Edge.StartVertex(edge) incoming_vertices.append(Graph.NearestVertex(graph, sv)) return incoming_vertices
def IsBipartite(graph, tolerance=0.0001)
-
Returns True if the input graph is bipartite. Returns False otherwise. See https://en.wikipedia.org/wiki/Bipartite_graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
bool
- True if the input graph is complete. False otherwise
Expand source code
@staticmethod def IsBipartite(graph, tolerance=0.0001): """ Returns True if the input graph is bipartite. Returns False otherwise. See https://en.wikipedia.org/wiki/Bipartite_graph. Parameters ---------- graph : topologic_core.Graph The input graph. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- bool True if the input graph is complete. False otherwise """ # From https://www.geeksforgeeks.org/bipartite-graph/ # This code is contributed by divyesh072019. from topologicpy.Topology import Topology def isBipartite(V, adj): # vector to store colour of vertex # assigning all to -1 i.e. uncoloured # colours are either 0 or 1 # for understanding take 0 as red and 1 as blue col = [-1]*(V) # queue for BFS storing {vertex , colour} q = [] #loop incase graph is not connected for i in range(V): # if not coloured if (col[i] == -1): # colouring with 0 i.e. red q.append([i, 0]) col[i] = 0 while len(q) != 0: p = q[0] q.pop(0) # current vertex v = p[0] # colour of current vertex c = p[1] # traversing vertexes connected to current vertex for j in adj[v]: # if already coloured with parent vertex color # then bipartite graph is not possible if (col[j] == c): return False # if uncoloured if (col[j] == -1): # colouring with opposite color to that of parent if c == 1: col[j] = 0 else: col[j] = 1 q.append([j, col[j]]) # if all vertexes are coloured such that # no two connected vertex have same colours return True if not Topology.IsInstance(graph, "Graph"): print("Graph.IsBipartite - Error: The input graph is not a valid graph. Returning None.") return None order = Graph.Order(graph) adjList = Graph.AdjacencyList(graph, tolerance=tolerance) return isBipartite(order, adjList)
def IsComplete(graph)
-
Returns True if the input graph is complete. Returns False otherwise. See https://en.wikipedia.org/wiki/Complete_graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
bool
- True if the input graph is complete. False otherwise
Expand source code
@staticmethod def IsComplete(graph): """ Returns True if the input graph is complete. Returns False otherwise. See https://en.wikipedia.org/wiki/Complete_graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- bool True if the input graph is complete. False otherwise """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IsComplete - Error: The input graph is not a valid graph. Returning None.") return None return graph.IsComplete()
def IsErdoesGallai(graph, sequence)
-
Returns True if the input sequence satisfies the Erdős–Gallai theorem. Returns False otherwise. See https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Gallai_theorem.
Parameters
graph
:topologic_core.Graph
- The input graph.
sequence
:list
- The input sequence.
Returns
bool
- True if the input sequence satisfies the Erdős–Gallai theorem. False otherwise.
Expand source code
@staticmethod def IsErdoesGallai(graph, sequence): """ Returns True if the input sequence satisfies the Erdős–Gallai theorem. Returns False otherwise. See https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Gallai_theorem. Parameters ---------- graph : topologic_core.Graph The input graph. sequence : list The input sequence. Returns ------- bool True if the input sequence satisfies the Erdős–Gallai theorem. False otherwise. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IsErdoesGallai - Error: The input graph is not a valid graph. Returning None.") return None return graph.IsErdoesGallai(sequence)
def IsolatedVertices(graph)
-
Returns the list of isolated vertices in the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
list
- The list of isolated vertices.
Expand source code
@staticmethod def IsolatedVertices(graph): """ Returns the list of isolated vertices in the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- list The list of isolated vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IsolatedVertices - Error: The input graph is not a valid graph. Returning None.") return None vertices = [] _ = graph.IsolatedVertices(vertices) return vertices
def JSONData(graph, verticesKey='vertices', edgesKey='edges', vertexLabelKey='', edgeLabelKey='', sourceKey='source', targetKey='target', xKey='x', yKey='y', zKey='z', geometryKey='brep', mantissa=6)
-
Converts the input graph into JSON data.
Parameters
graph
:topologic_core.Graph
- The input graph.
verticesKey
:str
, optional- The desired key name to call vertices. The default is "vertices".
edgesKey
:str
, optional- The desired key name to call edges. The default is "edges".
vertexLabelKey
:str
, optional- If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique.
edgeLabelKey
:str
, optional- If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique.
sourceKey
:str
, optional- The dictionary key used to store the source vertex. The default is "source".
targetKey
:str
, optional- The dictionary key used to store the target vertex. The default is "source".
xKey
:str
, optional- The desired key name to use for x-coordinates. The default is "x".
yKey
:str
, optional- The desired key name to use for y-coordinates. The default is "y".
zKey
:str
, optional- The desired key name to use for z-coordinates. The default is "z".
geometryKey
:str
, optional- The desired key name to use for geometry. The default is "brep".
mantissa
:int
, optional- The desired length of the mantissa. The default is 6.
Returns
dict
- The JSON data
Expand source code
@staticmethod def JSONData(graph, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", sourceKey="source", targetKey="target", xKey="x", yKey="y", zKey="z", geometryKey="brep", mantissa=6): """ Converts the input graph into JSON data. Parameters ---------- graph : topologic_core.Graph The input graph. verticesKey : str , optional The desired key name to call vertices. The default is "vertices". edgesKey : str , optional The desired key name to call edges. The default is "edges". vertexLabelKey : str , optional If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique. edgeLabelKey : str , optional If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique. sourceKey : str , optional The dictionary key used to store the source vertex. The default is "source". targetKey : str , optional The dictionary key used to store the target vertex. The default is "source". xKey : str , optional The desired key name to use for x-coordinates. The default is "x". yKey : str , optional The desired key name to use for y-coordinates. The default is "y". zKey : str , optional The desired key name to use for z-coordinates. The default is "z". geometryKey : str , optional The desired key name to use for geometry. The default is "brep". mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- dict The JSON data """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Helper import Helper vertices = Graph.Vertices(graph) j_data = {} j_data[verticesKey] = {} j_data[edgesKey] = {} n = max(len(str(len(vertices))), 4) v_labels = [] v_dicts = [] for i, v in enumerate(vertices): d = Topology.Dictionary(v) d = Dictionary.SetValueAtKey(d, xKey, Vertex.X(v, mantissa=mantissa)) d = Dictionary.SetValueAtKey(d, yKey, Vertex.Y(v, mantissa=mantissa)) d = Dictionary.SetValueAtKey(d, zKey, Vertex.Z(v, mantissa=mantissa)) if geometryKey: v_d = Topology.Dictionary(v) brep = Dictionary.ValueAtKey(v_d,"brep") if brep: d = Dictionary.SetValueAtKey(d, geometryKey, brep) v_dict = Dictionary.PythonDictionary(d) v_label = Dictionary.ValueAtKey(d, vertexLabelKey) if isinstance(v_label, str): v_label = Dictionary.ValueAtKey(d, vertexLabelKey) else: v_label = "Vertex_"+str(i).zfill(n) v_labels.append(v_label) v_dicts.append(v_dict) v_labels = Helper.MakeUnique(v_labels) for i, v_label in enumerate(v_labels): j_data[verticesKey][v_label] = v_dicts[i] edges = Graph.Edges(graph) n = len(str(len(edges))) e_labels = [] e_dicts = [] for i, e in enumerate(edges): sv = Edge.StartVertex(e) ev = Edge.EndVertex(e) svi = Vertex.Index(sv, vertices) evi = Vertex.Index(ev, vertices) sv_label = v_labels[svi] ev_label = v_labels[evi] d = Topology.Dictionary(e) d = Dictionary.SetValueAtKey(d, sourceKey, sv_label) d = Dictionary.SetValueAtKey(d, targetKey, ev_label) e_dict = Dictionary.PythonDictionary(d) e_label = Dictionary.ValueAtKey(d, edgeLabelKey) if isinstance(e_label, str): e_label = Dictionary.ValueAtKey(d, edgeLabelKey) else: e_label = "Edge_"+str(i).zfill(n) e_labels.append(e_label) e_dicts.append(e_dict) e_labels = Helper.MakeUnique(e_labels) for i, e_label in enumerate(e_labels): j_data[edgesKey][e_label] = e_dicts[i] return j_data
def JSONString(graph, verticesKey='vertices', edgesKey='edges', vertexLabelKey='', edgeLabelKey='', xKey='x', yKey='y', zKey='z', indent=4, sortKeys=False, mantissa=6)
-
Converts the input graph into JSON data.
Parameters
graph
:topologic_core.Graph
- The input graph.
verticesKey
:str
, optional- The desired key name to call vertices. The default is "vertices".
edgesKey
:str
, optional- The desired key name to call edges. The default is "edges".
vertexLabelKey
:str
, optional- If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique.
edgeLabelKey
:str
, optional- If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique.
xKey
:str
, optional- The desired key name to use for x-coordinates. The default is "x".
yKey
:str
, optional- The desired key name to use for y-coordinates. The default is "y".
zKey
:str
, optional- The desired key name to use for z-coordinates. The default is "z".
indent
:int
, optional- The desired amount of indent spaces to use. The default is 4.
sortKeys
:bool
, optional- If set to True, the keys will be sorted. Otherwise, they won't be. The default is False.
mantissa
:int
, optional- The desired length of the mantissa. The default is 6.
Returns
str
- The JSON str
Expand source code
@staticmethod def JSONString(graph, verticesKey="vertices", edgesKey="edges", vertexLabelKey="", edgeLabelKey="", xKey = "x", yKey = "y", zKey = "z", indent=4, sortKeys=False, mantissa=6): """ Converts the input graph into JSON data. Parameters ---------- graph : topologic_core.Graph The input graph. verticesKey : str , optional The desired key name to call vertices. The default is "vertices". edgesKey : str , optional The desired key name to call edges. The default is "edges". vertexLabelKey : str , optional If set to a valid string, the vertex label will be set to the value at this key. Otherwise it will be set to Vertex_XXXX where XXXX is a sequential unique number. Note: If vertex labels are not unique, they will be forced to be unique. edgeLabelKey : str , optional If set to a valid string, the edge label will be set to the value at this key. Otherwise it will be set to Edge_XXXX where XXXX is a sequential unique number. Note: If edge labels are not unique, they will be forced to be unique. xKey : str , optional The desired key name to use for x-coordinates. The default is "x". yKey : str , optional The desired key name to use for y-coordinates. The default is "y". zKey : str , optional The desired key name to use for z-coordinates. The default is "z". indent : int , optional The desired amount of indent spaces to use. The default is 4. sortKeys : bool , optional If set to True, the keys will be sorted. Otherwise, they won't be. The default is False. mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- str The JSON str """ import json json_data = Graph.JSONData(graph, verticesKey=verticesKey, edgesKey=edgesKey, vertexLabelKey=vertexLabelKey, edgeLabelKey=edgeLabelKey, xKey=xKey, yKey=yKey, zKey=zKey, mantissa=mantissa) json_string = json.dumps(json_data, indent=indent, sort_keys=sortKeys) return json_string
def LocalClusteringCoefficient(graph, vertices=None, mantissa=6)
-
Returns the local clustering coefficient of the input list of vertices within the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertices
:list
, optional- The input list of vertices. If set to None, the local clustering coefficient of all vertices will be computed.
mantissa
:int
, optional- The desired length of the mantissa. The default is 6.
Returns
list
- The list of local clustering coefficient. The order of the list matches the order of the list of input vertices.
Expand source code
@staticmethod def LocalClusteringCoefficient(graph, vertices=None, mantissa=6): """ Returns the local clustering coefficient of the input list of vertices within the input graph. See https://en.wikipedia.org/wiki/Clustering_coefficient. Parameters ---------- graph : topologic_core.Graph The input graph. vertices : list , optional The input list of vertices. If set to None, the local clustering coefficient of all vertices will be computed. mantissa : int , optional The desired length of the mantissa. The default is 6. Returns ------- list The list of local clustering coefficient. The order of the list matches the order of the list of input vertices. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology def local_clustering_coefficient(adjacency_matrix, node): """ Compute the local clustering coefficient for a given node in a graph represented by an adjacency matrix. Parameters: - adjacency_matrix: 2D list representing the adjacency matrix of the graph - node: Node for which the local clustering coefficient is computed Returns: - Local clustering coefficient for the given node """ neighbors = [i for i, value in enumerate(adjacency_matrix[node]) if value == 1] num_neighbors = len(neighbors) if num_neighbors < 2: # If the node has less than 2 neighbors, the clustering coefficient is undefined return 0.0 # Count the number of connections between the neighbors num_connections = 0 for i in range(num_neighbors): for j in range(i + 1, num_neighbors): if adjacency_matrix[neighbors[i]][neighbors[j]] == 1: num_connections += 1 # Calculate the local clustering coefficient local_clustering_coeff = 2.0 * num_connections / (num_neighbors * (num_neighbors - 1)) return local_clustering_coeff if not Topology.IsInstance(graph, "Graph"): print("Graph.LocalClusteringCoefficient - Error: The input graph parameter is not a valid graph. Returning None.") return None if vertices == None: vertices = Graph.Vertices(graph) if Topology.IsInstance(vertices, "Vertex"): vertices = [vertices] vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")] if len(vertices) < 1: print("Graph.LocalClusteringCoefficient - Error: The input vertices parameter does not contain valid vertices. Returning None.") return None g_vertices = Graph.Vertices(graph) adjacency_matrix = Graph.AdjacencyMatrix(graph) lcc = [] for v in vertices: i = Vertex.Index(v, g_vertices) if not i == None: lcc.append(round(local_clustering_coefficient(adjacency_matrix, i), mantissa)) else: lcc.append(None) return lcc
def LongestPath(graph, vertexA, vertexB, vertexKey=None, edgeKey=None, costKey=None, timeLimit=10, tolerance=0.0001)
-
Returns the longest path that connects the input vertices.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
vertexKey
:str
, optional- The vertex key to maximize. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value must be numeric. The default is None.
edgeKey
:str
, optional- The edge key to maximize. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length".
costKey
:str
, optional- If not None, the total cost of the longest_path will be stored in its dictionary under this key. The default is None.
timeLimit
:int
, optional- The time limit in second. The default is 10 seconds.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Wire
- The longest path between the input vertices.
Expand source code
@staticmethod def LongestPath(graph, vertexA, vertexB, vertexKey=None, edgeKey=None, costKey=None, timeLimit=10, tolerance=0.0001): """ Returns the longest path that connects the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. vertexKey : str , optional The vertex key to maximize. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value must be numeric. The default is None. edgeKey : str , optional The edge key to maximize. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the longest path that maximizes the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length". costKey : str , optional If not None, the total cost of the longest_path will be stored in its dictionary under this key. The default is None. timeLimit : int , optional The time limit in second. The default is 10 seconds. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Wire The longest path between the input vertices. """ from topologicpy. Dictionary import Dictionary from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Wire import Wire from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology from topologicpy.Helper import Helper if not Topology.IsInstance(graph, "Graph"): print("Graph.LongestPath - Error: the input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.LongestPath - Error: the input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.LongestPath - Error: the input vertexB is not a valid vertex. Returning None.") return None g_edges = Graph.Edges(graph) paths = Graph.AllPaths(graph, vertexA, vertexB, timeLimit=timeLimit) if not paths: print("Graph.LongestPath - Error: Could not find any paths within the specified time limit. Returning None.") return None if len(paths) < 1: print("Graph.LongestPath - Error: Could not find any paths within the specified time limit. Returning None.") return None if edgeKey == None: lengths = [len(Topology.Edges(path)) for path in paths] elif edgeKey.lower() == "length": lengths = [Wire.Length(path) for path in paths] else: lengths = [] for path in paths: edges = Topology.Edges(path) pathCost = 0 for edge in edges: index = Edge.Index(edge, g_edges) d = Topology.Dictionary(g_edges[index]) value = Dictionary.ValueAtKey(d, edgeKey) if not value == None: pathCost += value lengths.append(pathCost) if not vertexKey == None: g_vertices = Graph.Vertices(graph) for i, path in enumerate(paths): vertices = Topology.Vertices(path) pathCost = 0 for vertex in vertices: index = Vertex.Index(vertex, g_vertices) d = Topology.Dictionary(g_vertices[index]) value = Dictionary.ValueAtKey(d, vertexKey) if not value == None: pathCost += value lengths[i] += pathCost new_paths = Helper.Sort(paths, lengths) temp_path = new_paths[-1] cost = lengths[-1] new_edges = [] for edge in Topology.Edges(temp_path): new_edges.append(g_edges[Edge.Index(edge, g_edges)]) longest_path = Topology.SelfMerge(Cluster.ByTopologies(new_edges), tolerance=tolerance) sv = Topology.Vertices(longest_path)[0] if Vertex.Distance(sv, vertexB) < tolerance: # Wire is reversed. Re-reverse it if Topology.IsInstance(longest_path, "Edge"): longest_path = Edge.Reverse(longest_path) elif Topology.IsInstance(longest_path, "Wire"): longest_path = Wire.Reverse(longest_path) longest_path = Wire.OrientEdges(longest_path, Wire.StartVertex(longest_path), tolerance=tolerance) if not costKey == None: lengths.sort() d = Dictionary.ByKeysValues([costKey], [cost]) longest_path = Topology.SetDictionary(longest_path, d) return longest_path
def MaximumDelta(graph)
-
Returns the maximum delta of the input graph. The maximum delta of a graph is the maximum degree of a vertex in the graph.
Parameters
graph
:topologic_core.Graph
- the input graph.
Returns
int
- The maximum delta.
Expand source code
@staticmethod def MaximumDelta(graph): """ Returns the maximum delta of the input graph. The maximum delta of a graph is the maximum degree of a vertex in the graph. Parameters ---------- graph : topologic_core.Graph the input graph. Returns ------- int The maximum delta. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.MaximumDelta - Error: The input graph is not a valid graph. Returning None.") return None return graph.MaximumDelta()
def MaximumFlow(graph, source, sink, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=False, residualKey='residual', tolerance=0.0001)
-
Returns the maximum flow of the input graph. See https://en.wikipedia.org/wiki/Maximum_flow_problem
Parameters
graph
:topologic_core.Graph
- The input graph. This is assumed to be a directed graph
source
:topologic_core.Vertex
- The input source vertex.
sink
:topologic_core.Vertex
- The input sink/target vertex.
edgeKeyFwd
:str
, optional- The edge dictionary key to use to find the value of the forward capacity of the edge. If not set, the length of the edge is used as its capacity. The default is None.
edgeKeyBwd
:str
, optional- The edge dictionary key to use to find the value of the backward capacity of the edge. This is only considered if the edge is set to be bidrectional. The default is None.
bidirKey
:str
, optional- The edge dictionary key to use to determine if the edge is bidrectional. The default is None.
bidrectional
:bool
, optional- If set to True, the whole graph is considered to be bidirectional. The default is False.
residualKey
:str
, optional- The name of the key to use to store the residual value of each edge capacity in the input graph. The default is "residual".
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
float
- The maximum flow.
Expand source code
@staticmethod def MaximumFlow(graph, source, sink, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=False, residualKey="residual", tolerance=0.0001): """ Returns the maximum flow of the input graph. See https://en.wikipedia.org/wiki/Maximum_flow_problem Parameters ---------- graph : topologic_core.Graph The input graph. This is assumed to be a directed graph source : topologic_core.Vertex The input source vertex. sink : topologic_core.Vertex The input sink/target vertex. edgeKeyFwd : str , optional The edge dictionary key to use to find the value of the forward capacity of the edge. If not set, the length of the edge is used as its capacity. The default is None. edgeKeyBwd : str , optional The edge dictionary key to use to find the value of the backward capacity of the edge. This is only considered if the edge is set to be bidrectional. The default is None. bidirKey : str , optional The edge dictionary key to use to determine if the edge is bidrectional. The default is None. bidrectional : bool , optional If set to True, the whole graph is considered to be bidirectional. The default is False. residualKey : str , optional The name of the key to use to store the residual value of each edge capacity in the input graph. The default is "residual". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- float The maximum flow. """ from topologicpy.Vertex import Vertex from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology # Using BFS as a searching algorithm def searching_algo_BFS(adjMatrix, s, t, parent): visited = [False] * (len(adjMatrix)) queue = [] queue.append(s) visited[s] = True while queue: u = queue.pop(0) for ind, val in enumerate(adjMatrix[u]): if visited[ind] == False and val > 0: queue.append(ind) visited[ind] = True parent[ind] = u return True if visited[t] else False # Applying fordfulkerson algorithm def ford_fulkerson(adjMatrix, source, sink): am = adjMatrix.copy() row = len(am) parent = [-1] * (row) max_flow = 0 while searching_algo_BFS(am, source, sink, parent): path_flow = float("Inf") s = sink while(s != source): path_flow = min(path_flow, am[parent[s]][s]) s = parent[s] # Adding the path flows max_flow += path_flow # Updating the residual values of edges v = sink while(v != source): u = parent[v] am[u][v] -= path_flow am[v][u] += path_flow v = parent[v] return [max_flow, am] if edgeKeyFwd == None: useEdgeLength = True else: useEdgeLength = False adjMatrix = Graph.AdjacencyMatrix(graph, edgeKeyFwd=edgeKeyFwd, edgeKeyBwd=edgeKeyBwd, bidirKey=bidirKey, bidirectional=bidirectional, useEdgeIndex = False, useEdgeLength=useEdgeLength, tolerance=tolerance) edgeMatrix = Graph.AdjacencyMatrix(graph, edgeKeyFwd=edgeKeyFwd, edgeKeyBwd=edgeKeyBwd, bidirKey=bidirKey, bidirectional=bidirectional, useEdgeIndex = True, useEdgeLength=False, tolerance=tolerance) vertices = Graph.Vertices(graph) edges = Graph.Edges(graph) sourceIndex = Vertex.Index(source, vertices) sinkIndex = Vertex.Index(sink, vertices) max_flow, am = ford_fulkerson(adjMatrix=adjMatrix, source=sourceIndex, sink=sinkIndex) for i in range(len(am)): row = am[i] for j in range(len(row)): residual = am[i][j] edge = edges[edgeMatrix[i][j]-1] d = Topology.Dictionary(edge) if not d == None: keys = Dictionary.Keys(d) values = Dictionary.Values(d) else: keys = [] values = [] keys.append(residualKey) values.append(residual) d = Dictionary.ByKeysValues(keys, values) edge = Topology.SetDictionary(edge, d) return max_flow
def MeshData(g)
-
Returns the mesh data of the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
dict
- The python dictionary of the mesh data of the input graph. The keys in the dictionary are: 'vertices' : The list of [x, y, z] coordinates of the vertices. 'edges' : the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j]. 'vertexDictionaries' : The python dictionaries of the vertices (in the same order as the list of vertices). 'edgeDictionaries' : The python dictionaries of the edges (in the same order as the list of edges).
Expand source code
@staticmethod def MeshData(g): """ Returns the mesh data of the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- dict The python dictionary of the mesh data of the input graph. The keys in the dictionary are: 'vertices' : The list of [x, y, z] coordinates of the vertices. 'edges' : the list of [i, j] indices into the vertices list to signify and edge that connects vertices[i] to vertices[j]. 'vertexDictionaries' : The python dictionaries of the vertices (in the same order as the list of vertices). 'edgeDictionaries' : The python dictionaries of the edges (in the same order as the list of edges). """ from topologicpy.Vertex import Vertex from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology g_vertices = Graph.Vertices(g) m_vertices = [] v_dicts = [] for g_vertex in g_vertices: m_vertices.append(Vertex.Coordinates(g_vertex)) d = Dictionary.PythonDictionary(Topology.Dictionary(g_vertex)) v_dicts.append(d) g_edges = Graph.Edges(g) m_edges = [] e_dicts = [] for g_edge in g_edges: sv = g_edge.StartVertex() ev = g_edge.EndVertex() si = Vertex.Index(sv, g_vertices) ei = Vertex.Index(ev, g_vertices) m_edges.append([si, ei]) d = Dictionary.PythonDictionary(Topology.Dictionary(g_edge)) e_dicts.append(d) return {'vertices':m_vertices, 'edges': m_edges, 'vertexDictionaries': v_dicts, 'edgeDictionaries': e_dicts }
def MinimumDelta(graph)
-
Returns the minimum delta of the input graph. The minimum delta of a graph is the minimum degree of a vertex in the graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
int
- The minimum delta.
Expand source code
@staticmethod def MinimumDelta(graph): """ Returns the minimum delta of the input graph. The minimum delta of a graph is the minimum degree of a vertex in the graph. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The minimum delta. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.MinimumDelta - Error: The input graph is not a valid graph. Returning None.") return None return graph.MinimumDelta()
def MinimumSpanningTree(graph, edgeKey=None, tolerance=0.0001)
-
Returns the minimum spanning tree of the input graph. See https://en.wikipedia.org/wiki/Minimum_spanning_tree.
Parameters
graph
:topologic_core.Graph
- The input graph.
edgeKey
:string
, optional- If set, the value of the edgeKey will be used as the weight and the tree will minimize the weight. The value associated with the edgeKey must be numerical. If the key is not set, the edges will be sorted by their length. The default is None
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The minimum spanning tree.
Expand source code
@staticmethod def MinimumSpanningTree(graph, edgeKey=None, tolerance=0.0001): """ Returns the minimum spanning tree of the input graph. See https://en.wikipedia.org/wiki/Minimum_spanning_tree. Parameters ---------- graph : topologic_core.Graph The input graph. edgeKey : string , optional If set, the value of the edgeKey will be used as the weight and the tree will minimize the weight. The value associated with the edgeKey must be numerical. If the key is not set, the edges will be sorted by their length. The default is None tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The minimum spanning tree. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology def vertexInList(vertex, vertexList, tolerance=0.0001): for v in vertexList: if Vertex.Distance(v, vertex) < tolerance: return True return False if not Topology.IsInstance(graph, "Graph"): print("Graph.MinimumSpanningTree - Error: The input graph is not a valid graph. Returning None.") return None edges = Graph.Edges(graph) vertices = Graph.Vertices(graph) values = [] if isinstance(edgeKey, str): for edge in edges: d = Dictionary.Dictionary(edge) value = Dictionary.ValueAtKey(d, edgeKey) if value == None or not isinstance(value, int) or not isinstance(value, float): return None values.append(value) else: for edge in edges: value = Edge.Length(edge) values.append(value) keydict = dict(zip(edges, values)) edges.sort(key=keydict.get) mst = Graph.ByVerticesEdges(vertices,[]) for edge in edges: sv = Edge.StartVertex(edge) ev = Edge.EndVertex(edge) if len(Graph.Vertices(mst)) > 0: if not Graph.Path(mst, Graph.NearestVertex(mst, sv), Graph.NearestVertex(mst, ev)): d = Topology.Dictionary(edge) if len(Dictionary.Keys(d)) > 0: tranEdgeDicts = True else: tranEdgeDicts = False mst = Graph.AddEdge(mst, edge, transferVertexDictionaries=False, transferEdgeDictionaries=tranEdgeDicts) return mst
-
Creates a 2D navigation graph.
Parameters
face
:topologic_core.Face
- The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles
sources
:list
- The first input list of sources (vertices). Navigation edges will connect these veritces to destinations.
destinations
:list
- The input list of destinations (vertices). Navigation edges will connect these vertices to sources.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
tqdm
:bool
, optional- If set to True, a tqdm progress bar is shown. Otherwise, it is not. The default is True.
Returns
topologic_core.Graph
- The navigation graph.
Expand source code
@staticmethod def NavigationGraph(face, sources=None, destinations=None, tolerance=0.0001, progressBar=True): """ Creates a 2D navigation graph. Parameters ---------- face : topologic_core.Face The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles sources : list The first input list of sources (vertices). Navigation edges will connect these veritces to destinations. destinations : list The input list of destinations (vertices). Navigation edges will connect these vertices to sources. tolerance : float , optional The desired tolerance. The default is 0.0001. tqdm : bool , optional If set to True, a tqdm progress bar is shown. Otherwise, it is not. The default is True. Returns ------- topologic_core.Graph The navigation graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Wire import Wire from topologicpy.Face import Face from topologicpy.Graph import Graph from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology import topologic_core as topologic from tqdm.auto import tqdm if not Topology.IsInstance(face, "Face"): print("Graph.NavigationGraph - Error: The input face parameter is not a valid face. Returning None") return None if sources == None: sources = Topology.Vertices(face) if destinations == None: destinations = Topology.Vertices(face) if not isinstance(sources, list): print("Graph.NavigationGraph - Error: The input sources parameter is not a valid list. Returning None") return None if not isinstance(destinations, list): print("Graph.NavigationGraph - Error: The input destinations parameter is not a valid list. Returning None") return None sources = [v for v in sources if Topology.IsInstance(v, "Vertex")] if len(sources) < 1: print("Graph.NavigationGraph - Error: The input sources parameter does not contain any vertices. Returning None") return None destinations = [v for v in destinations if Topology.IsInstance(v, "Vertex")] #if len(destinations) < 1: #Nothing to navigate to, so return a graph made of sources #return Graph.ByVerticesEdges(sources, []) # Add obstuse angles of external boundary to viewpoints e_boundary = Face.ExternalBoundary(face) if Topology.IsInstance(e_boundary, "Wire"): vertices = Topology.Vertices(e_boundary) interior_angles = Wire.InteriorAngles(e_boundary) for i, ang in enumerate(interior_angles): if ang > 180: sources.append(vertices[i]) destinations.append(vertices[i]) i_boundaries = Face.InternalBoundaries(face) for i_boundary in i_boundaries: if Topology.IsInstance(i_boundary, "Wire"): vertices = Topology.Vertices(i_boundary) interior_angles = Wire.InteriorAngles(i_boundary) for i, ang in enumerate(interior_angles): if ang < 180: sources.append(vertices[i]) destinations.append(vertices[i]) used = [] for i in range(max(len(sources), len(destinations))): temp_row = [] for j in range(max(len(sources), len(destinations))): temp_row.append(0) used.append(temp_row) final_edges = [] if progressBar: the_range = tqdm(range(len(sources))) else: the_range = range(len(sources)) for i in the_range: source = sources[i] index_b = Vertex.Index(source, destinations) for j in range(len(destinations)): destination = destinations[j] index_a = Vertex.Index(destination, sources) if used[i][j] == 1 or used[j][i]: continue if Vertex.Distance(source, destination) > tolerance: edge = Edge.ByVertices([source,destination]) e = Topology.Boolean(edge, face, operation="intersect") if Topology.IsInstance(e, "Edge"): final_edges.append(edge) used[i][j] = 1 if not index_a == None and not index_b == None: used[j][i] = 1 if len(i_boundaries) > 0: holes_edges = Topology.Edges(Cluster.ByTopologies(i_boundaries)) final_edges += holes_edges if len(final_edges) > 0: final_vertices = Topology.Vertices(Cluster.ByTopologies(final_edges)) g = Graph.ByVerticesEdges(final_vertices, final_edges) return g return None
def NearestVertex(graph, vertex)
-
Returns the vertex in the input graph that is the nearest to the input vertex.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
Returns
topologic_core.Vertex
- The vertex in the input graph that is the nearest to the input vertex.
Expand source code
@staticmethod def NearestVertex(graph, vertex): """ Returns the vertex in the input graph that is the nearest to the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. Returns ------- topologic_core.Vertex The vertex in the input graph that is the nearest to the input vertex. """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.NearestVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.NearestVertex - Error: The input vertex is not a valid vertex. Returning None.") return None vertices = Graph.Vertices(graph) nearestVertex = vertices[0] nearestDistance = Vertex.Distance(vertex, nearestVertex) for aGraphVertex in vertices: newDistance = Vertex.Distance(vertex, aGraphVertex) if newDistance < nearestDistance: nearestDistance = newDistance nearestVertex = aGraphVertex return nearestVertex
def NetworkXGraph(graph, tolerance=0.0001)
-
converts the input graph into a NetworkX Graph. See http://networkx.org
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
networkX Graph
- The created networkX Graph
Expand source code
@staticmethod def NetworkXGraph(graph, tolerance=0.0001): """ converts the input graph into a NetworkX Graph. See http://networkx.org Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- networkX Graph The created networkX Graph """ from topologicpy.Vertex import Vertex from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary try: import networkx as nx except: print("Graph.NetworkXGraph - Information: Installing required networkx library.") try: os.system("pip install networkx") except: os.system("pip install networkx --user") try: import networkx as nx print("Graph.NetworkXGraph - Infromation: networkx library installed correctly.") except: warnings.warn("Graph - Error: Could not import networkx. Please try to install networkx manually. Returning None.") return None if not Topology.IsInstance(graph, "Graph"): print("Graph.NetworkXGraph - Error: The input graph is not a valid graph. Returning None.") return None nxGraph = nx.Graph() vertices = Graph.Vertices(graph) order = len(vertices) nodes = [] for i in range(order): v = vertices[i] d = Topology.Dictionary(vertices[i]) if d: keys = Dictionary.Keys(d) if not keys: keys = [] values = Dictionary.Values(d) if not values: values = [] keys += ["x","y","z"] import random values += [Vertex.X(v), Vertex.Y(v), Vertex.Z(v)] d = Dictionary.ByKeysValues(keys,values) pythonD = Dictionary.PythonDictionary(d) nodes.append((i, pythonD)) else: nodes.append((i, {"name": str(i)})) nxGraph.add_nodes_from(nodes) for i in range(order): v = vertices[i] adjVertices = Graph.AdjacentVertices(graph, vertices[i]) for adjVertex in adjVertices: adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance) if not adjIndex == None: nxGraph.add_edge(i,adjIndex, length=(Vertex.Distance(v, adjVertex))) pos=nx.spring_layout(nxGraph, k=0.2) nx.set_node_attributes(nxGraph, pos, "pos") return nxGraph
def Order(graph)
-
Returns the graph order of the input graph. The graph order is its number of vertices.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
int
- The number of vertices in the input graph
Expand source code
@staticmethod def Order(graph): """ Returns the graph order of the input graph. The graph order is its number of vertices. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The number of vertices in the input graph """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Order - Error: The input graph is not a valid graph. Returning None.") return None return len(Graph.Vertices(graph))
def OutgoingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) ‑> list
-
Returns the outgoing edges connected to a vertex. An edge is considered outgoing if its start vertex is coincident with the input vertex.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
directed
:bool
, optional- If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of outgoing edges
Expand source code
@staticmethod def OutgoingEdges(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the outgoing edges connected to a vertex. An edge is considered outgoing if its start vertex is coincident with the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of outgoing edges """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.IncomingEdges - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.IncomingEdges - Error: The input vertex parameter is not a valid vertex. Returning None.") return None edges = Graph.Edges(graph, [vertex]) if directed == False: return edges outgoing_edges = [] for edge in edges: sv = Edge.StartVertex(edge) if Vertex.Distance(vertex, sv) < tolerance: outgoing_edges.append(edge) return outgoing_edges
def OutgoingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) ‑> list
-
Returns the list of outgoing vertices connected to a vertex. A vertex is considered outgoing if it is an adjacent vertex to the input vertex and the the edge connecting it to the input vertex is an outgoing edge.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
directed
:bool
, optional- If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of incoming vertices
Expand source code
@staticmethod def OutgoingVertices(graph, vertex, directed: bool = False, tolerance: float = 0.0001) -> list: """ Returns the list of outgoing vertices connected to a vertex. A vertex is considered outgoing if it is an adjacent vertex to the input vertex and the the edge connecting it to the input vertex is an outgoing edge. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. directed : bool , optional If set to True, the graph is considered to be directed. Otherwise, it will be considered as an unidrected graph. The default is False. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of incoming vertices """ from topologicpy.Edge import Edge from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.OutgoingVertices - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.OutgoingVertices - Error: The input vertex parameter is not a valid vertex. Returning None.") return None if directed == False: return Graph.AdjacentVertices(graph, vertex) outgoing_edges = Graph.OutgoingEdges(graph, vertex, directed=directed, tolerance=tolerance) outgoing_vertices = [] for edge in outgoing_edges: ev = Edge.EndVertex(edge) outgoing_vertices.append(Graph.NearestVertex(graph, ev)) return outgoing_vertices
def PageRank(graph, alpha=0.85, maxIterations=100, normalize=True, directed=False, mantissa=6, tolerance=0.0001)
-
Calculates PageRank scores for nodes in a directed graph. see https://en.wikipedia.org/wiki/PageRank.
Parameters
graph
:topologic_core.Graph
- The input graph.
alpha
:float
, optional- The damping (dampening) factor. The default is 0.85. See https://en.wikipedia.org/wiki/PageRank.
maxIterations
:int
, optional- The maximum number of iterations to calculate the page rank. The default is 100.
normalize
:bool
, optional- If set to True, the results will be normalized from 0 to 1. Otherwise, they won't be. The default is True.
directed
:bool
, optional- If set to True, the graph is considered as a directed graph. Otherwise, it will be considered as an undirected graph. The default is False.
mantissa
:int
, optional- The desired length of the mantissa.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of page ranks for the vertices in the graph.
Expand source code
@staticmethod def PageRank(graph, alpha=0.85, maxIterations=100, normalize=True, directed=False, mantissa=6, tolerance=0.0001): """ Calculates PageRank scores for nodes in a directed graph. see https://en.wikipedia.org/wiki/PageRank. Parameters ---------- graph : topologic_core.Graph The input graph. alpha : float , optional The damping (dampening) factor. The default is 0.85. See https://en.wikipedia.org/wiki/PageRank. maxIterations : int , optional The maximum number of iterations to calculate the page rank. The default is 100. normalize : bool , optional If set to True, the results will be normalized from 0 to 1. Otherwise, they won't be. The default is True. directed : bool , optional If set to True, the graph is considered as a directed graph. Otherwise, it will be considered as an undirected graph. The default is False. mantissa : int , optional The desired length of the mantissa. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of page ranks for the vertices in the graph. """ from topologicpy.Vertex import Vertex from topologicpy.Helper import Helper vertices = Graph.Vertices(graph) num_vertices = len(vertices) if num_vertices < 1: print("Graph.PageRank - Error: The input graph parameter has no vertices. Returning None") return None initial_score = 1.0 / num_vertices scores = [initial_score for vertex in vertices] for _ in range(maxIterations): new_scores = [0 for vertex in vertices] for i, vertex in enumerate(vertices): incoming_score = 0 for incoming_vertex in Graph.IncomingVertices(graph, vertex, directed=directed): if len(Graph.IncomingVertices(graph, incoming_vertex, directed=directed)) > 0: incoming_score += scores[Vertex.Index(incoming_vertex, vertices)] / len(Graph.IncomingVertices(graph, incoming_vertex, directed=directed)) new_scores[i] = alpha * incoming_score + (1 - alpha) / num_vertices # Check for convergence if all(abs(new_scores[i] - scores[i]) < tolerance for i in range(len(vertices))): break scores = new_scores if normalize == True: scores = Helper.Normalize(scores, mantissa=mantissa) else: scores = [round(x, mantissa) for x in scores] return scores
def Path(graph, vertexA, vertexB, tolerance=0.0001)
-
Returns a path (wire) in the input graph that connects the input vertices.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Wire
- The path (wire) in the input graph that connects the input vertices.
Expand source code
@staticmethod def Path(graph, vertexA, vertexB, tolerance=0.0001): """ Returns a path (wire) in the input graph that connects the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. tolerance : float, optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Wire The path (wire) in the input graph that connects the input vertices. """ from topologicpy.Wire import Wire from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Path - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.Path - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.Path - Error: The input vertexB is not a valid vertex. Returning None.") return None path = graph.Path(vertexA, vertexB) if Topology.IsInstance(path, "Wire"): path = Wire.OrientEdges(path, Wire.StartVertex(path), tolerance=tolerance) return path
def PyvisGraph(graph, path, overwrite=True, height=900, backgroundColor='white', fontColor='black', notebook=False, vertexSize=6, vertexSizeKey=None, vertexColor='black', vertexColorKey=None, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=None, minVertexGroup=None, maxVertexGroup=None, edgeLabelKey=None, edgeWeight=0, edgeWeightKey=None, showNeighbours=True, selectMenu=True, filterMenu=True, colorScale='viridis')
-
Displays a pyvis graph. See https://pyvis.readthedocs.io/.
Parameters
graph
:topologic_core.Graph
- The input graph.
path
:str
- The desired file path to the HTML file into which to save the pyvis graph.
overwrite
:bool
, optional- If set to True, the HTML file is overwritten.
height
:int
, optional- The desired figure height in pixels. The default is 900 pixels.
backgroundColor
:str
, optional- The desired background color for the figure. This can be a named color or a hexadecimal value. The default is 'white'.
fontColor
:str
, optional- The desired font color for the figure. This can be a named color or a hexadecimal value. The default is 'black'.
notebook
:bool
, optional- If set to True, the figure will be targeted at a Jupyter Notebook. Note that this is not working well. Pyvis has bugs. The default is False.
vertexSize
:int
, optional- The desired default vertex size. The default is 6.
vertexSizeKey
:str
, optional- If not set to None, the vertex size will be derived from the dictionary value set at this key. If set to "degree", the size of the vertex will be determined by its degree (number of neighbors). The default is None.
vertexColor
:int
, optional- The desired default vertex color. his can be a named color or a hexadecimal value. The default is 'black'.
vertexColorKey
:str
, optional- If not set to None, the vertex color will be derived from the dictionary value set at this key. The default is None.
vertexLabelKey
:str
, optional- If not set to None, the vertex label will be derived from the dictionary value set at this key. The default is None.
vertexGroupKey
:str
, optional- If not set to None, the vertex color will be determined by the group the vertex belongs to as derived from the value set at this key. The default is None.
vertexGroups
:list
, optional- The list of all possible vertex groups. This will help in vertex coloring. The default is None.
minVertexGroup
:int
orfloat
, optional- If the vertex groups are numeric, specify the minimum value you wish to consider for vertex coloring. The default is None.
maxVertexGroup
:int
orfloat
, optional- If the vertex groups are numeric, specify the maximum value you wish to consider for vertex coloring. The default is None.
edgeWeight
:int
, optional- The desired default weight of the edge. This determines its thickness. The default is 0.
edgeWeightKey
:str
, optional- If not set to None, the edge weight will be derived from the dictionary value set at this key. If set to "length" or "distance", the weight of the edge will be determined by its geometric length. The default is None.
edgeLabelKey
:str
, optional- If not set to None, the edge label will be derived from the dictionary value set at this key. The default is None.
showNeighbors
:bool
, optional- If set to True, a list of neighbors is shown when you hover over a vertex. The default is True.
selectMenu
:bool
, optional- If set to True, a selection menu will be displayed. The default is True
filterMenu
:bool
, optional- If set to True, a filtering menu will be displayed. The default is True.
colorScale
:str
, optional- The desired type of plotly color scales to use (e.g. "viridis", "plasma"). The default is "viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
Returns
None
- The pyvis graph is displayed either inline (notebook mode) or in a new browser window or tab.
Expand source code
@staticmethod def PyvisGraph(graph, path, overwrite=True, height=900, backgroundColor="white", fontColor="black", notebook=False, vertexSize=6, vertexSizeKey=None, vertexColor="black", vertexColorKey=None, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=None, minVertexGroup=None, maxVertexGroup=None, edgeLabelKey=None, edgeWeight=0, edgeWeightKey=None, showNeighbours=True, selectMenu=True, filterMenu=True, colorScale="viridis"): """ Displays a pyvis graph. See https://pyvis.readthedocs.io/. Parameters ---------- graph : topologic_core.Graph The input graph. path : str The desired file path to the HTML file into which to save the pyvis graph. overwrite : bool , optional If set to True, the HTML file is overwritten. height : int , optional The desired figure height in pixels. The default is 900 pixels. backgroundColor : str, optional The desired background color for the figure. This can be a named color or a hexadecimal value. The default is 'white'. fontColor : str , optional The desired font color for the figure. This can be a named color or a hexadecimal value. The default is 'black'. notebook : bool , optional If set to True, the figure will be targeted at a Jupyter Notebook. Note that this is not working well. Pyvis has bugs. The default is False. vertexSize : int , optional The desired default vertex size. The default is 6. vertexSizeKey : str , optional If not set to None, the vertex size will be derived from the dictionary value set at this key. If set to "degree", the size of the vertex will be determined by its degree (number of neighbors). The default is None. vertexColor : int , optional The desired default vertex color. his can be a named color or a hexadecimal value. The default is 'black'. vertexColorKey : str , optional If not set to None, the vertex color will be derived from the dictionary value set at this key. The default is None. vertexLabelKey : str , optional If not set to None, the vertex label will be derived from the dictionary value set at this key. The default is None. vertexGroupKey : str , optional If not set to None, the vertex color will be determined by the group the vertex belongs to as derived from the value set at this key. The default is None. vertexGroups : list , optional The list of all possible vertex groups. This will help in vertex coloring. The default is None. minVertexGroup : int or float , optional If the vertex groups are numeric, specify the minimum value you wish to consider for vertex coloring. The default is None. maxVertexGroup : int or float , optional If the vertex groups are numeric, specify the maximum value you wish to consider for vertex coloring. The default is None. edgeWeight : int , optional The desired default weight of the edge. This determines its thickness. The default is 0. edgeWeightKey : str, optional If not set to None, the edge weight will be derived from the dictionary value set at this key. If set to "length" or "distance", the weight of the edge will be determined by its geometric length. The default is None. edgeLabelKey : str , optional If not set to None, the edge label will be derived from the dictionary value set at this key. The default is None. showNeighbors : bool , optional If set to True, a list of neighbors is shown when you hover over a vertex. The default is True. selectMenu : bool , optional If set to True, a selection menu will be displayed. The default is True filterMenu : bool , optional If set to True, a filtering menu will be displayed. The default is True. colorScale : str , optional The desired type of plotly color scales to use (e.g. "viridis", "plasma"). The default is "viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/. Returns ------- None The pyvis graph is displayed either inline (notebook mode) or in a new browser window or tab. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary from topologicpy.Color import Color from os.path import exists try: from pyvis.network import Network except: print("Graph.PyvisGraph - Information: Installing required pyvis library.") try: os.system("pip install pyvis") except: os.system("pip install pyvis --user") try: from pyvis.network import Network print("Graph.PyvisGraph - Information: pyvis library installed correctly.") except: warnings.warn("Graph - Error: Could not import pyvis. Please try to install pyvis manually. Retruning None.") return None net = Network(height=str(height)+"px", width="100%", bgcolor=backgroundColor, font_color=fontColor, select_menu=selectMenu, filter_menu=filterMenu, cdn_resources="remote", notebook=notebook) if notebook == True: net.prep_notebook() vertices = Graph.Vertices(graph) edges = Graph.Edges(graph) nodes = [i for i in range(len(vertices))] if not vertexLabelKey == None: node_labels = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexLabelKey) for v in vertices] else: node_labels = list(range(len(vertices))) if not vertexColorKey == None: colors = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexColorKey) for v in vertices] else: colors = [vertexColor for v in vertices] node_titles = [str(n) for n in node_labels] group = "" if not vertexGroupKey == None: colors = [] if vertexGroups: if len(vertexGroups) > 0: if type(vertexGroups[0]) == int or type(vertexGroups[0]) == float: if not minVertexGroup: minVertexGroup = min(vertexGroups) if not maxVertexGroup: maxVertexGroup = max(vertexGroups) else: minVertexGroup = 0 maxVertexGroup = len(vertexGroups) - 1 else: minVertexGroup = 0 maxVertexGroup = 1 for m, v in enumerate(vertices): group = "" d = Topology.Dictionary(v) if d: try: group = Dictionary.ValueAtKey(d, key=vertexGroupKey) or None except: group = "" try: if type(group) == int or type(group) == float: if group < minVertexGroup: group = minVertexGroup if group > maxVertexGroup: group = maxVertexGroup color = Color.RGBToHex(Color.ByValueInRange(group, minValue=minVertexGroup, maxValue=maxVertexGroup, colorScale=colorScale)) else: color = Color.RGBToHex(Color.ByValueInRange(vertexGroups.index(group), minValue=minVertexGroup, maxValue=maxVertexGroup, colorScale=colorScale)) colors.append(color) except: colors.append(vertexColor) net.add_nodes(nodes, label=node_labels, title=node_titles, color=colors) for e in edges: edge_label = "" if not edgeLabelKey == None: d = Topology.Dictionary(e) edge_label = Dictionary.ValueAtKey(d, edgeLabelKey) if edge_label == None: edge_label = "" w = edgeWeight if not edgeWeightKey == None: d = Topology.Dictionary(e) if edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance": w = Edge.Length(e) else: weightValue = Dictionary.ValueAtKey(d, edgeWeightKey) if weightValue: w = weightValue sv = Edge.StartVertex(e) ev = Edge.EndVertex(e) svi = Vertex.Index(sv, vertices) evi = Vertex.Index(ev, vertices) net.add_edge(svi, evi, weight=w, label=edge_label) net.inherit_edge_colors(False) # add neighbor data to node hover data and compute vertexSize if showNeighbours == True or not vertexSizeKey == None: for i, node in enumerate(net.nodes): if showNeighbours == True: neighbors = list(net.neighbors(node["id"])) neighbor_labels = [str(net.nodes[n]["id"])+": "+str(net.nodes[n]["label"]) for n in neighbors] node["title"] = str(node["id"])+": "+node["title"]+"\n" node["title"] += "Neighbors:\n" + "\n".join(neighbor_labels) vs = vertexSize if not vertexSizeKey == None: d = Topology.Dictionary(vertices[i]) if vertexSizeKey.lower() == "neighbours" or vertexSizeKey.lower() == "degree": temp_vs = Graph.VertexDegree(graph, vertices[i]) else: temp_vs = Dictionary.ValueAtKey(vertices[i], vertexSizeKey) if temp_vs: vs = temp_vs node["value"] = vs # Make sure the file extension is .html ext = path[len(path)-5:len(path)] if ext.lower() != ".html": path = path+".html" if not overwrite and exists(path): print("Graph.PyvisGraph - Error: a file already exists at the specified path and overwrite is set to False. Returning None.") return None if overwrite == True: net.save_graph(path) net.show_buttons() net.show(path, notebook=notebook) return None
def RemoveEdge(graph, edge, tolerance=0.0001)
-
Removes the input edge from the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
edge
:topologic_core.Edge
- The input edge.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph with the input edge removed.
Expand source code
@staticmethod def RemoveEdge(graph, edge, tolerance=0.0001): """ Removes the input edge from the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. edge : topologic_core.Edge The input edge. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input edge removed. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.RemoveEdge - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(edge, "Edge"): print("Graph.RemoveEdge - Error: The input edge is not a valid edge. Returning None.") return None _ = graph.RemoveEdges([edge], tolerance) return graph
def RemoveVertex(graph, vertex, tolerance=0.0001)
-
Removes the input vertex from the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The input graph with the input vertex removed.
Expand source code
@staticmethod def RemoveVertex(graph, vertex, tolerance=0.0001): """ Removes the input vertex from the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The input graph with the input vertex removed. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.RemoveVertex - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.RemoveVertex - Error: The input vertex is not a valid vertex. Returning None.") return None graphVertex = Graph.NearestVertex(graph, vertex) _ = graph.RemoveVertices([graphVertex]) return graph
def SetDictionary(graph, dictionary)
-
Sets the input graph's dictionary to the input dictionary
Parameters
graph
:topologic_core.Graph
- The input graph.
dictionary
:topologic_core.Dictionary
ordict
- The input dictionary.
Returns
topologic_core.Graph
- The input graph with the input dictionary set in it.
Expand source code
@staticmethod def SetDictionary(graph, dictionary): """ Sets the input graph's dictionary to the input dictionary Parameters ---------- graph : topologic_core.Graph The input graph. dictionary : topologic_core.Dictionary or dict The input dictionary. Returns ------- topologic_core.Graph The input graph with the input dictionary set in it. """ from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.SetDictionary - Error: the input graph parameter is not a valid graph. Returning None.") return None if isinstance(dictionary, dict): dictionary = Dictionary.ByPythonDictionary(dictionary) if not Topology.IsInstance(dictionary, "Dictionary"): print("Graph.SetDictionary - Warning: the input dictionary parameter is not a valid dictionary. Returning original input.") return graph if len(dictionary.Keys()) < 1: print("Graph.SetDictionary - Warning: the input dictionary parameter is empty. Returning original input.") return graph _ = graph.SetDictionary(dictionary) return graph
def ShortestPath(graph, vertexA, vertexB, vertexKey='', edgeKey='Length', tolerance=0.0001)
-
Returns the shortest path that connects the input vertices. The shortest path will take into consideration both the vertexKey and the edgeKey if both are specified and will minimize the total "cost" of the path. Otherwise, it will take into consideration only whatever key is specified.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
vertexKey
:string
, optional- The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None.
edgeKey
:string
, optional- The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length".
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Wire
- The shortest path between the input vertices.
Expand source code
@staticmethod def ShortestPath(graph, vertexA, vertexB, vertexKey="", edgeKey="Length", tolerance=0.0001): """ Returns the shortest path that connects the input vertices. The shortest path will take into consideration both the vertexKey and the edgeKey if both are specified and will minimize the total "cost" of the path. Otherwise, it will take into consideration only whatever key is specified. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. vertexKey : string , optional The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None. edgeKey : string , optional The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length". tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Wire The shortest path between the input vertices. """ from topologicpy.Vertex import Vertex from topologicpy.Wire import Wire from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.ShortestPath - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.ShortestPath - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.ShortestPath - Error: The input vertexB is not a valid vertex. Returning None.") return None if edgeKey: if edgeKey.lower() == "length": edgeKey = "Length" try: gsv = Graph.NearestVertex(graph, vertexA) gev = Graph.NearestVertex(graph, vertexB) shortest_path = graph.ShortestPath(gsv, gev, vertexKey, edgeKey) if Topology.IsInstance(shortest_path, "Edge"): shortest_path = Wire.ByEdges([shortest_path]) sv = Topology.Vertices(shortest_path)[0] if Vertex.Distance(sv, gev) < tolerance: # Path is reversed. Correct it. if Topology.IsInstance(shortest_path, "Wire"): shortest_path = Wire.Reverse(shortest_path) shortest_path = Wire.OrientEdges(shortest_path, Wire.StartVertex(shortest_path), tolerance=tolerance) return shortest_path except: return None
def ShortestPaths(graph, vertexA, vertexB, vertexKey='', edgeKey='length', timeLimit=10, pathLimit=10, tolerance=0.0001)
-
Returns the shortest path that connects the input vertices.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
vertexKey
:string
, optional- The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None.
edgeKey
:string
, optional- The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length".
timeLimit
:int
, optional- The search time limit in seconds. The default is 10 seconds
pathLimit
:int
, optional- The number of found paths limit. The default is 10 paths.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
list
- The list of shortest paths between the input vertices.
Expand source code
@staticmethod def ShortestPaths(graph, vertexA, vertexB, vertexKey="", edgeKey="length", timeLimit=10, pathLimit=10, tolerance=0.0001): """ Returns the shortest path that connects the input vertices. Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. vertexKey : string , optional The vertex key to minimise. If set the vertices dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value must be numeric. The default is None. edgeKey : string , optional The edge key to minimise. If set the edges dictionaries will be searched for this key and the associated value will be used to compute the shortest path that minimized the total value. The value of the key must be numeric. If set to "length" (case insensitive), the shortest path by length is computed. The default is "length". timeLimit : int , optional The search time limit in seconds. The default is 10 seconds pathLimit: int , optional The number of found paths limit. The default is 10 paths. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- list The list of shortest paths between the input vertices. """ from topologicpy.Topology import Topology def isUnique(paths, path): if path == None: return False if len(paths) < 1: return True for aPath in paths: copyPath = topologic.Topology.DeepCopy(aPath) # Hook to Core dif = copyPath.Difference(path, False) if dif == None: return False return True if not Topology.IsInstance(graph, "Graph"): print("Graph.ShortestPaths - Error: The input graph parameter is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.ShortestPaths - Error: The input vertexA parameter is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.ShortestPaths - Error: The input vertexB parameter is not a valid vertex. Returning None.") return None shortestPaths = [] end = time.time() + timeLimit while time.time() < end and len(shortestPaths) < pathLimit: if (graph != None): if edgeKey: if edgeKey.lower() == "length": edgeKey = "Length" shortest_path = Graph.ShortestPath(graph, vertexA, vertexB, vertexKey=vertexKey, edgeKey=edgeKey, tolerance=tolerance) # Find the first shortest path if isUnique(shortestPaths, shortest_path): shortestPaths.append(shortest_path) vertices = Graph.Vertices(graph) random.shuffle(vertices) edges = Graph.Edges(graph) graph = Graph.ByVerticesEdges(vertices, edges) return shortestPaths
def Show(graph, vertexColor='black', vertexSize=6, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=[], showVertices=True, showVertexLegend=False, edgeColor='black', edgeWidth=1, edgeLabelKey=None, edgeGroupKey=None, edgeGroups=[], showEdges=True, showEdgeLegend=False, colorScale='viridis', renderer=None, width=950, height=500, xAxis=False, yAxis=False, zAxis=False, axisSize=1, backgroundColor='rgba(0,0,0,0)', marginLeft=0, marginRight=0, marginTop=20, marginBottom=0, camera=[-1.25, -1.25, 1.25], center=[0, 0, 0], up=[0, 0, 1], projection='perspective', tolerance=0.0001)
-
Shows the graph using Plotly.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexColor
:str
, optional- The desired color of the output vertices. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "black".
vertexSize
:float
, optional- The desired size of the vertices. The default is 1.1.
vertexLabelKey
:str
, optional- The dictionary key to use to display the vertex label. The default is None.
vertexGroupKey
:str
, optional- The dictionary key to use to display the vertex group. The default is None.
vertexGroups
:list
, optional- The list of vertex groups against which to index the color of the vertex. The default is [].
showVertices
:bool
, optional- If set to True the vertices will be drawn. Otherwise, they will not be drawn. The default is True.
showVertexLegend
:bool
, optional- If set to True the vertex legend will be drawn. Otherwise, it will not be drawn. The default is False.
edgeColor
:str
, optional- The desired color of the output edges. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "black".
edgeWidth
:float
, optional- The desired thickness of the output edges. The default is 1.
edgeLabelKey
:str
, optional- The dictionary key to use to display the edge label. The default is None.
edgeGroupKey
:str
, optional- The dictionary key to use to display the edge group. The default is None.
showEdges
:bool
, optional- If set to True the edges will be drawn. Otherwise, they will not be drawn. The default is True.
showEdgeLegend
:bool
, optional- If set to True the edge legend will be drawn. Otherwise, it will not be drawn. The default is False.
colorScale
:str
, optional- The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
renderer
:str
, optional- The desired renderer. See Plotly.Renderers(). If set to None, the code will attempt to discover the most suitable renderer. The default is None.
width
:int
, optional- The width in pixels of the figure. The default value is 950.
height
:int
, optional- The height in pixels of the figure. The default value is 950.
xAxis
:bool
, optional- If set to True the x axis is drawn. Otherwise it is not drawn. The default is False.
yAxis
:bool
, optional- If set to True the y axis is drawn. Otherwise it is not drawn. The default is False.
zAxis
:bool
, optional- If set to True the z axis is drawn. Otherwise it is not drawn. The default is False.
axisSize
:float
, optional- The size of the X, Y, Z, axes. The default is 1.
backgroundColor
:str
, optional- The desired color of the background. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "rgba(0,0,0,0)".
marginLeft
:int
, optional- The size in pixels of the left margin. The default value is 0.
marginRight
:int
, optional- The size in pixels of the right margin. The default value is 0.
marginTop
:int
, optional- The size in pixels of the top margin. The default value is 20.
marginBottom
:int
, optional- The size in pixels of the bottom margin. The default value is 0.
camera
:list
, optional- The desired location of the camera). The default is [-1.25, -1.25, 1.25].
center
:list
, optional- The desired center (camera target). The default is [0, 0, 0].
up
:list
, optional- The desired up vector. The default is [0, 0, 1].
projection
:str
, optional- The desired type of projection. The options are "orthographic" or "perspective". It is case insensitive. The default is "perspective"
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
None
Expand source code
@staticmethod def Show(graph, vertexColor="black", vertexSize=6, vertexLabelKey=None, vertexGroupKey=None, vertexGroups=[], showVertices=True, showVertexLegend=False, edgeColor="black", edgeWidth=1, edgeLabelKey=None, edgeGroupKey=None, edgeGroups=[], showEdges=True, showEdgeLegend=False, colorScale='viridis', renderer=None, width=950, height=500, xAxis=False, yAxis=False, zAxis=False, axisSize=1, backgroundColor='rgba(0,0,0,0)', marginLeft=0, marginRight=0, marginTop=20, marginBottom=0, camera=[-1.25, -1.25, 1.25], center=[0, 0, 0], up=[0, 0, 1], projection="perspective", tolerance=0.0001): """ Shows the graph using Plotly. Parameters ---------- graph : topologic_core.Graph The input graph. vertexColor : str , optional The desired color of the output vertices. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "black". vertexSize : float , optional The desired size of the vertices. The default is 1.1. vertexLabelKey : str , optional The dictionary key to use to display the vertex label. The default is None. vertexGroupKey : str , optional The dictionary key to use to display the vertex group. The default is None. vertexGroups : list , optional The list of vertex groups against which to index the color of the vertex. The default is []. showVertices : bool , optional If set to True the vertices will be drawn. Otherwise, they will not be drawn. The default is True. showVertexLegend : bool , optional If set to True the vertex legend will be drawn. Otherwise, it will not be drawn. The default is False. edgeColor : str , optional The desired color of the output edges. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "black". edgeWidth : float , optional The desired thickness of the output edges. The default is 1. edgeLabelKey : str , optional The dictionary key to use to display the edge label. The default is None. edgeGroupKey : str , optional The dictionary key to use to display the edge group. The default is None. showEdges : bool , optional If set to True the edges will be drawn. Otherwise, they will not be drawn. The default is True. showEdgeLegend : bool , optional If set to True the edge legend will be drawn. Otherwise, it will not be drawn. The default is False. colorScale : str , optional The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/. renderer : str , optional The desired renderer. See Plotly.Renderers(). If set to None, the code will attempt to discover the most suitable renderer. The default is None. width : int , optional The width in pixels of the figure. The default value is 950. height : int , optional The height in pixels of the figure. The default value is 950. xAxis : bool , optional If set to True the x axis is drawn. Otherwise it is not drawn. The default is False. yAxis : bool , optional If set to True the y axis is drawn. Otherwise it is not drawn. The default is False. zAxis : bool , optional If set to True the z axis is drawn. Otherwise it is not drawn. The default is False. axisSize : float , optional The size of the X, Y, Z, axes. The default is 1. backgroundColor : str , optional The desired color of the background. This can be any plotly color string and may be specified as: - A hex string (e.g. '#ff0000') - An rgb/rgba string (e.g. 'rgb(255,0,0)') - An hsl/hsla string (e.g. 'hsl(0,100%,50%)') - An hsv/hsva string (e.g. 'hsv(0,100%,100%)') - A named CSS color. The default is "rgba(0,0,0,0)". marginLeft : int , optional The size in pixels of the left margin. The default value is 0. marginRight : int , optional The size in pixels of the right margin. The default value is 0. marginTop : int , optional The size in pixels of the top margin. The default value is 20. marginBottom : int , optional The size in pixels of the bottom margin. The default value is 0. camera : list , optional The desired location of the camera). The default is [-1.25, -1.25, 1.25]. center : list , optional The desired center (camera target). The default is [0, 0, 0]. up : list , optional The desired up vector. The default is [0, 0, 1]. projection : str , optional The desired type of projection. The options are "orthographic" or "perspective". It is case insensitive. The default is "perspective" tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- None """ from topologicpy.Plotly import Plotly from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Show - Error: The input graph is not a valid graph. Returning None.") return None data= Plotly.DataByGraph(graph, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLegend=showEdgeLegend, colorScale=colorScale) fig = Plotly.FigureByData(data, width=width, height=height, xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize, backgroundColor=backgroundColor, marginLeft=marginLeft, marginRight=marginRight, marginTop=marginTop, marginBottom=marginBottom, tolerance=tolerance) Plotly.Show(fig, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
def Size(graph)
-
Returns the graph size of the input graph. The graph size is its number of edges.
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
int
- The number of edges in the input graph.
Expand source code
@staticmethod def Size(graph): """ Returns the graph size of the input graph. The graph size is its number of edges. Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- int The number of edges in the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Size - Error: The input graph is not a valid graph. Returning None.") return None return len(Graph.Edges(graph))
def TopologicalDistance(graph, vertexA, vertexB, tolerance=0.0001)
-
Returns the topological distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory).
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexA
:topologic_core.Vertex
- The first input vertex.
vertexB
:topologic_core.Vertex
- The second input vertex.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
int
- The topological distance between the input vertices.
Expand source code
@staticmethod def TopologicalDistance(graph, vertexA, vertexB, tolerance=0.0001): """ Returns the topological distance between the input vertices. See https://en.wikipedia.org/wiki/Distance_(graph_theory). Parameters ---------- graph : topologic_core.Graph The input graph. vertexA : topologic_core.Vertex The first input vertex. vertexB : topologic_core.Vertex The second input vertex. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- int The topological distance between the input vertices. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.TopologicalDistance - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertexA, "Vertex"): print("Graph.TopologicalDistance - Error: The input vertexA is not a valid vertex. Returning None.") return None if not Topology.IsInstance(vertexB, "Vertex"): print("Graph.TopologicalDistance - Error: The input vertexB is not a valid vertex. Returning None.") return None return graph.TopologicalDistance(vertexA, vertexB, tolerance)
def Topology(graph)
-
Returns the topology (cluster) of the input graph
Parameters
graph
:topologic_core.Graph
- The input graph.
Returns
topologic_core.Cluster
- The topology of the input graph.
Expand source code
@staticmethod def Topology(graph): """ Returns the topology (cluster) of the input graph Parameters ---------- graph : topologic_core.Graph The input graph. Returns ------- topologic_core.Cluster The topology of the input graph. """ from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Topology - Error: The input graph is not a valid graph. Returning None.") return None return graph.Topology()
def Tree(graph, vertex=None, tolerance=0.0001)
-
Creates a tree graph version of the input graph rooted at the input vertex.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
, optional- The input root vertex. If not set, the first vertex in the graph is set as the root vertex. The default is None.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The tree graph version of the input graph.
Expand source code
@staticmethod def Tree(graph, vertex=None, tolerance=0.0001): """ Creates a tree graph version of the input graph rooted at the input vertex. Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex , optional The input root vertex. If not set, the first vertex in the graph is set as the root vertex. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The tree graph version of the input graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Topology import Topology def vertexInList(vertex, vertexList): if vertex and vertexList: if Topology.IsInstance(vertex, "Vertex") and isinstance(vertexList, list): for i in range(len(vertexList)): if vertexList[i]: if Topology.IsInstance(vertexList[i], "Vertex"): if Topology.IsSame(vertex, vertexList[i]): return True return False def getChildren(vertex, parent, graph, vertices): children = [] adjVertices = [] if vertex: adjVertices = Graph.AdjacentVertices(graph, vertex) if parent == None: return adjVertices else: for aVertex in adjVertices: if (not vertexInList(aVertex, [parent])) and (not vertexInList(aVertex, vertices)): children.append(aVertex) return children def buildTree(graph, dictionary, vertex, parent, tolerance=0.0001): vertices = dictionary['vertices'] edges = dictionary['edges'] if not vertexInList(vertex, vertices): vertices.append(vertex) if parent: edge = Graph.Edge(graph, parent, vertex, tolerance) ev = Edge.EndVertex(edge) if Vertex.Distance(parent, ev) < tolerance: edge = Edge.Reverse(edge) edges.append(edge) if parent == None: parent = vertex children = getChildren(vertex, parent, graph, vertices) dictionary['vertices'] = vertices dictionary['edges'] = edges for child in children: dictionary = buildTree(graph, dictionary, child, vertex, tolerance) return dictionary if not Topology.IsInstance(graph, "Graph"): print("Graph.Tree - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): vertex = Graph.Vertices(graph)[0] else: vertex = Graph.NearestVertex(graph, vertex) dictionary = {'vertices':[], 'edges':[]} dictionary = buildTree(graph, dictionary, vertex, None, tolerance) return Graph.ByVerticesEdges(dictionary['vertices'], dictionary['edges'])
def VertexDegree(graph, vertex, edgeKey: str = None, tolerance: float = 0.0001)
-
Returns the degree of the input vertex. See https://en.wikipedia.org/wiki/Degree_(graph_theory).
Parameters
graph
:topologic_core.Graph
- The input graph.
vertex
:topologic_core.Vertex
- The input vertex.
edgeKey
:str
, optional- If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
int
- The degree of the input vertex.
Expand source code
@staticmethod def VertexDegree(graph, vertex, edgeKey: str = None, tolerance: float = 0.0001): """ Returns the degree of the input vertex. See https://en.wikipedia.org/wiki/Degree_(graph_theory). Parameters ---------- graph : topologic_core.Graph The input graph. vertex : topologic_core.Vertex The input vertex. edgeKey : str , optional If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- int The degree of the input vertex. """ from topologicpy.Topology import Topology from topologicpy.Dictionary import Dictionary import numbers if not Topology.IsInstance(graph, "Graph"): print("Graph.VertexDegree - Error: The input graph is not a valid graph. Returning None.") return None if not Topology.IsInstance(vertex, "Vertex"): print("Graph.VertexDegree - Error: The input vertex is not a valid vertex. Returning None.") return None if not isinstance(edgeKey, str): edgeKey = "" edges = Graph.Edges(graph, [vertex], tolerance=tolerance) degree = 0 for edge in edges: d = Topology.Dictionary(edge) value = Dictionary.ValueAtKey(d, edgeKey) if isinstance(value, numbers.Number): degree += value else: degree += 1 return degree
def Vertices(graph, vertexKey=None, reverse=False)
-
Returns the list of vertices in the input graph.
Parameters
graph
:topologic_core.Graph
- The input graph.
vertexKey
:str
, optional- If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None.
reverse
:bool
, optional- If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
Returns
list
- The list of vertices in the input graph.
Expand source code
@staticmethod def Vertices(graph, vertexKey=None, reverse=False): """ Returns the list of vertices in the input graph. Parameters ---------- graph : topologic_core.Graph The input graph. vertexKey : str , optional If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. The default is None. reverse : bool , optional If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False. Returns ------- list The list of vertices in the input graph. """ from topologicpy.Helper import Helper from topologicpy.Dictionary import Dictionary from topologicpy.Topology import Topology if not Topology.IsInstance(graph, "Graph"): print("Graph.Vertices - Error: The input graph is not a valid graph. Returning None.") return None vertices = [] if graph: try: _ = graph.Vertices(vertices) except: vertices = [] if not vertexKey == None: sorting_values = [] for v in vertices: d = Topology.Dictionary(v) value = Dictionary.ValueAtKey(d, vertexKey) sorting_values.append(value) vertices = Helper.Sort(vertices, sorting_values) if reverse == True: vertices.reverse() return vertices
def VisibilityGraph(face, viewpointsA=None, viewpointsB=None, tolerance=0.0001)
-
Creates a 2D visibility graph.
Parameters
face
:topologic_core.Face
- The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles
viewpointsA
:list
, optional- The first input list of viewpoints (vertices). Visibility edges will connect these veritces to viewpointsB. If set to None, this parameters will be set to all vertices of the input face. The default is None.
viewpointsB
:list
, optional- The input list of viewpoints (vertices). Visibility edges will connect these vertices to viewpointsA. If set to None, this parameters will be set to all vertices of the input face. The default is None.
tolerance
:float
, optional- The desired tolerance. The default is 0.0001.
Returns
topologic_core.Graph
- The visibility graph.
Expand source code
@staticmethod def VisibilityGraph(face, viewpointsA=None, viewpointsB=None, tolerance=0.0001): """ Creates a 2D visibility graph. Parameters ---------- face : topologic_core.Face The input boundary. View edges will be clipped to this face. The holes in the face are used as the obstacles viewpointsA : list , optional The first input list of viewpoints (vertices). Visibility edges will connect these veritces to viewpointsB. If set to None, this parameters will be set to all vertices of the input face. The default is None. viewpointsB : list , optional The input list of viewpoints (vertices). Visibility edges will connect these vertices to viewpointsA. If set to None, this parameters will be set to all vertices of the input face. The default is None. tolerance : float , optional The desired tolerance. The default is 0.0001. Returns ------- topologic_core.Graph The visibility graph. """ from topologicpy.Vertex import Vertex from topologicpy.Edge import Edge from topologicpy.Face import Face from topologicpy.Graph import Graph from topologicpy.Cluster import Cluster from topologicpy.Topology import Topology if not Topology.IsInstance(face, "Face"): print("Graph.VisibilityGraph - Error: The input face parameter is not a valid face. Returning None") return None if viewpointsA == None: viewpointsA = Topology.Vertices(face) if viewpointsB == None: viewpointsB = Topology.Vertices(face) if not isinstance(viewpointsA, list): print("Graph.VisibilityGraph - Error: The input viewpointsA parameter is not a valid list. Returning None") return None if not isinstance(viewpointsB, list): print("Graph.VisibilityGraph - Error: The input viewpointsB parameter is not a valid list. Returning None") return None viewpointsA = [v for v in viewpointsA if Topology.IsInstance(v, "Vertex")] if len(viewpointsA) < 1: print("Graph.VisibilityGraph - Error: The input viewpointsA parameter does not contain any vertices. Returning None") return None viewpointsB = [v for v in viewpointsB if Topology.IsInstance(v, "Vertex")] if len(viewpointsB) < 1: #Nothing to look at, so return a graph made of viewpointsA return Graph.ByVerticesEdges(viewpointsA, []) i_boundaries = Face.InternalBoundaries(face) obstacles = [] for i_boundary in i_boundaries: if Topology.IsInstance(i_boundary, "Wire"): obstacles.append(Face.ByWire(i_boundary)) if len(obstacles) > 0: obstacle_cluster = Cluster.ByTopologies(obstacles) else: obstacle_cluster = None def intersects_obstacles(edge, obstacle_cluster, tolerance=0.0001): result = Topology.Difference(edge, obstacle_cluster) if result == None: return True if Topology.IsInstance(result, "Cluster"): return True if Topology.IsInstance(result, "Edge"): if abs(Edge.Length(edge) - Edge.Length(result)) > tolerance: return True return False final_edges = [] for i in tqdm(range(len(viewpointsA))): va = viewpointsA[i] for j in range(len(viewpointsB)): vb = viewpointsB[j] if Vertex.Distance(va, vb) > tolerance: edge = Edge.ByVertices([va,vb]) if not intersects_obstacles(edge, obstacle_cluster): final_edges.append(edge) if len(final_edges) > 0: final_vertices = Topology.Vertices(Cluster.ByTopologies(final_edges)) g = Graph.ByVerticesEdges(final_vertices, final_edges) return g return None