def draw_small_multiples(nb_col, TPs, SM, lay):
    """
  Create a graph containing a number of thumbnails per line.
  
  @type     nb_col: Int
  @param   nb_col: Number of thumbnails by line
  @type       TPs: List
  @param      TPs: Time points name
  @type        SM: Tulip graph
  @param       SM: Small Multiples subgraph
  @type       lay: tlp.LayoutProperty
  @param      lay: Coord of nodes
  @note          : disable (better v2)
  """
    bb_tp = tlp.computeBoundingBox(SM)
    x = 0
    y = 0
    count = 0
    for gr in TPs:
        x = x + bb_tp.width() + 2000
        tp = SM.getSubGraph(gr)
        if count >= nb_col:
            x = bb_tp.width() + 2000
            y = y - bb_tp.height() - 2000
            count = 0
        for n in tp.getNodes():
            lay[n] = lay[n] + tlp.Vec3f(x, y, 0)
        for e in tp.getEdges():
            Ltmp = []
            for el in lay[e]:
                h = el + tlp.Vec3f(x, y, 0)
                Ltmp.append(h)
            lay[e] = Ltmp
        count += 1
Exemple #2
0
def constructGrid(smallMultGraph, columns):
    """ Function to create the grid of the small multiple graph, and displace each small multiple according to the layout of the grid. 

  To create the grid, the bounding box of the small multiple graph is computed. Hence we can get the width and the height of a graph.
  Those two parameters will be used to move each subgraph of the hierarchy in the small multiple graph according to their id order.
  The translattion will be function of the product of the column by the width for the x axis. And the product of the line by the negative
  form of the height for the y axis.For example, the subgraph tp1 will be placed in the coordinate (0,0) of the grid and then doesn't need to be moved. 
  But for tp2, this graph will be settled in the coordinate (1,1) of the grid and then be moved along the x axis by : 1 * width. This part
  of the algorithm is taken care by the function "drawSmallMultiple".

  Args:
    smallMultGraph (tlp.Graph) : the small multiple graph 
    columns (integer) : the number of columns for the grid 

  Returns:
    None
  """
    layout = smallMultGraph.getLayoutProperty("viewLayout")
    bBox = tlp.computeBoundingBox(smallMultGraph)
    numberSub = smallMultGraph.numberOfSubGraphs()
    idSubgraph = 0
    line = 0
    while idSubgraph < numberSub:
        for column in range(columns):
            if (idSubgraph < numberSub):
                idSubgraph += 1
                subGraph = smallMultGraph.getSubGraph(
                    "tp{}".format(idSubgraph))
                drawSmallMultiple(subGraph, line, column, bBox, layout)
            else:
                break
        line += 1
def draw_small_multiples_v2(nb_col, TPs, SM, lay):
    """
  Create a graph containing a number of thumbnails per line.
  
  Optimized algorithm to save around 5 seconds.  
  
  @type   nb_col: Int
  @param  nb_col: Number of thumbnails by line
  @type      TPs: List
  @param     TPs: Time points name
  @type       SM: Tulip graph
  @param      SM: Small Multiples subgraph
  @type      lay: tlp.LayoutProperty
  @param     lay: Coord of nodes
  """
    bb_tp = tlp.computeBoundingBox(SM)
    x = 0
    y = 0
    count = 0
    for gr in TPs:
        x = x + bb_tp.width() + 2000
        tp = SM.getSubGraph(gr)
        if count >= nb_col:
            x = bb_tp.width() + 2000
            y = y - bb_tp.height() - 2000
            count = 0
        count = count + 1
        lay.translate(tlp.Vec3f(x, y, 0), tp)
Exemple #4
0
def placeSmallMultiples(smallMultiplesTree, nbCol):
    bb = tlp.computeBoundingBox(smallMultiplesTree)
    #Multiply by 2 to have a shift between all the graphs
    xmax = bb[1][0] * 2
    ymax = bb[1][1] * 2

    #Shifts all the graphs according to the number of their associated timepoint, the number of columns choosen and the bounding box
    for sg in smallMultiplesTree.getSubGraphs():
        smallLayout = sg.getLayoutProperty("viewLayout")
        sgNames = sg.getName().split(" ")
        sgName = sgNames[0]
        nbSG = int(sgName[2:len(sgName)])

        for i in range(0, nbCol + 1):
            if (nbSG <= nbCol * (i + 1) and nbSG > nbCol * i):
                for node in sg.getNodes():
                    newPos = tlp.Coord(
                        smallLayout[node][0] + xmax * (nbSG - nbCol * i),
                        smallLayout[node][1] - ymax * i, smallLayout[node][2])
                    smallLayout.setNodeValue(node, newPos)

                for edge in sg.getEdges():
                    newEdgePos = []
                    for pos in smallLayout[edge]:
                        newPos = tlp.Coord(pos[0] + xmax * (nbSG - nbCol * i),
                                           pos[1] - ymax * i, pos[2])
                        newEdgePos.append(newPos)
                    smallLayout.setEdgeValue(edge, newEdgePos)
    def dfs(g, cur_height):
        node = {
            'id': g.getId(),
            'geometry': None,
            'diameter': 0,
            'desc_metanodes': {},
            'parent_metanode': None,
            'leaf_nodes': {},
            'level': cur_height
        }

        # For finding whether a node is in a subgraph
        for leaf in g.getNodes():
            node['leaf_nodes'][leaf.id] = True
        # For finding whether two meta-nodes are on the same path of the node hierarchy
        for s in g.getDescendantGraphs():
            node['desc_metanodes'][s.getId()] = True

        height['a'] = max(height['a'], cur_height + 1)
        for s in g.getSubGraphs():
            dfs(s, cur_height + 1)
            metanodes[s.getId()]['parent_metanode'] = g.getId()

        # Add the field parent_metanode to the leaf node
        for leaf in g.getNodes():
            tmp = leaf_nodes[leaf.id]
            if tmp['parent_metanode'] is None:
                tmp['parent_metanode'] = g.getId()

        # Compute convex hull of this sub-graph in post-order
        if bounding_shape == 'convex_hull':
            if g.numberOfSubGraphs() == 0:
                # compute a convex hull of its leaf nodes
                coords = tlp.computeConvexHull(g)
                node['geometry'] = Polygon([(c.x(), c.y()) for c in coords])
            else:
                # union the convex hull of its sub-graphs
                node['geometry'] = MultiPolygon([
                    metanodes[s.getId()]['geometry'] for s in g.getSubGraphs()
                ]).convex_hull

            # Note that we don't compute the real diameter for a polygon, but instead, only use the diagonal of
            # the axis aligned bounding box to approximate the diameter, which is cheap to compute
            bbox = node['geometry'].bounds
            node['diameter'] = Point(bbox[0],
                                     bbox[1]).distance(Point(bbox[2], bbox[3]))
        elif bounding_shape == 'circle':
            center, fur = tlp.computeBoundingRadius(g)
            radius = center.dist(fur)
            node['geometry'] = Point(center.x(), center.y()).buffer(
                radius, cap_style=CAP_STYLE.round)
            node['diameter'] = 2 * radius
        else:
            bbox = tlp.computeBoundingBox(g)
            node['geometry'] = Polygon([(c.x(), c.y()) for c in bbox])
            node['diameter'] = bbox[0].dist(bbox[1])

        metanodes[g.getId()] = node
Exemple #6
0
def draw_small_multiples_v2(nb_col, TPs, SM, lay):
    #TODO dire qu'on a gagné du temps par rapport à l'autre v1
    bb_tp = tlp.computeBoundingBox(SM)
    x = 0
    y = 0
    count = 0
    for gr in TPs:
        x = x + bb_tp.width() + 2000
        tp = SM.getSubGraph(gr)
        #lay = tp.getLayoutProperty("viewlayout")
        if count >= nb_col:
            x = bb_tp.width() + 2000
            y = y - bb_tp.height() - 2000
            count = 0
        count = count + 1
        lay.translate(tlp.Vec3f(x, y, 0), tp)
Exemple #7
0
def positionSmallMultiples(g, smallG, columnNumber):
    layout = g.getLayoutProperty("viewLayout")
    graphBoundingBox = tlp.computeBoundingBox(g)
    gHeight = graphBoundingBox.height()
    gWidth = graphBoundingBox.width()
    n = 1
    y = 0
    line = 0
    for smallImg in smallG.getSubGraphs():
        if n == columnNumber + 1:
            line += 1
            y = -gHeight * line * 1.5
            n = 1
        x = gWidth * n * 1.5
        newCenter = tlp.Vec3f(x, y, 0)
        layout.center(newCenter, smallImg)
        n += 1
def subgraph_grid(multiple_graph, nbcolumn):
    """
    Align all the subgraph of a graph, in a grid.
    
    Author:
        Pierre Jacquet
        Modfied by Eliot Ragueneau

    Args:
        multiple_graph (tlp.Graph): A parent graph
        nbcolumn (int): number of column in the grid
    """
    # get one subgraph's bounding box
    bounding_box = tlp.computeBoundingBox(multiple_graph.getNthSubGraph(1))
    size_x = 1.5 * abs(bounding_box[1][0] - bounding_box[0][0])
    size_y = 1.5 * abs(bounding_box[1][1] - bounding_box[0][1])
    # Multiplied by 1.5 to have an separation between graphs

    number_of_visited_subgraph = 0
    offset_x = 0
    offset_y = 0

    layout = multiple_graph.getLayoutProperty("viewLayout")
    for sub_graph in multiple_graph.getSubGraphs():
        number_of_visited_subgraph += 1
        for node in sub_graph.getNodes():
            layout[node] += tlp.Vec3f(offset_x, -offset_y,
                                      0)  # Move the node by offsets values
        for edge in sub_graph.getEdges():
            control_points = layout[edge]
            new_control_points = []
            for vector in control_points:
                new_control_points.append(
                    tuple(map(sum, zip(vector, (offset_x, -offset_y, 0)))))
            layout[edge] = new_control_points
        # Calculate the new offset value
        offset_x = number_of_visited_subgraph % nbcolumn * size_x
        offset_y = (number_of_visited_subgraph // nbcolumn) * size_y
Exemple #9
0
def draw_small_multiples(nb_col, TPs, SM, lay):
    """
  """
    bb_tp = tlp.computeBoundingBox(SM)
    x = 0
    y = 0
    count = 0
    for gr in TPs:
        x = x + bb_tp.width() + 2000
        tp = SM.getSubGraph(gr)
        if count >= nb_col:
            x = bb_tp.width() + 2000
            y = y - bb_tp.height() - 2000
            count = 0
        for n in tp.getNodes():
            lay[n] = lay[n] + tlp.Vec3f(x, y, 0)
        for e in tp.getEdges():
            Ltmp = []
            for el in lay[e]:
                h = el + tlp.Vec3f(x, y, 0)
                Ltmp.append(h)
            lay[e] = Ltmp
        count += 1
def convert(input_path, leaf_only=False, bounding_shape='convex_hull'):
    graph = tlp.loadGraph(input_path)

    # Retrieve nodes and edges from graph and construct Shapely geometries
    view_layout = graph.getLayoutProperty('viewLayout')
    view_size = graph.getSizeProperty('viewSize')

    leaf_nodes = [
        {
            'id':
            n.id,
            'parent_metanode':
            None,  # fulfill later
            'geometry':
            Point(view_layout[n].x(),
                  view_layout[n].y()).buffer(view_size[n][0] / 2.0,
                                             cap_style=CAP_STYLE.round),
            'diameter':
            view_size[n][0]
        } for n in graph.nodes()
    ]

    node_mapping = {}
    for n in leaf_nodes:
        node_mapping[n['id']] = n

    edges = []
    check_dup = {}
    for e in graph.getEdges():
        src, tgt = graph.ends(e)
        if src.id > tgt.id:
            tmp = src
            src = tgt
            tgt = tmp
        edge_id = '{}-{}'.format(src.id, tgt.id)

        # remove duplicate and self-connecting edges
        if edge_id not in check_dup and src.id != tgt.id:
            edges.append({
                'id':
                e.id,
                'ends': (src.id, tgt.id),
                'geometry':
                chop_segment(node_mapping[src.id]['geometry'],
                             node_mapping[tgt.id]['geometry'])
            })
            check_dup[edge_id] = True

    bbox = tlp.computeBoundingBox(graph)
    root = graph.getId()
    height = {'a': 1}
    metanodes = {}

    # Construct a simple node (graph) hierarchy data structure from the tulip graph and count levels
    # This is the same level counting method in the Bourqui multi-level force layout paper.

    def dfs(g, cur_height):
        node = {
            'id': g.getId(),
            'geometry': None,
            'diameter': 0,
            'desc_metanodes': {},
            'parent_metanode': None,
            'leaf_nodes': {},
            'level': cur_height
        }

        # For finding whether a node is in a subgraph
        for leaf in g.getNodes():
            node['leaf_nodes'][leaf.id] = True
        # For finding whether two meta-nodes are on the same path of the node hierarchy
        for s in g.getDescendantGraphs():
            node['desc_metanodes'][s.getId()] = True

        height['a'] = max(height['a'], cur_height + 1)
        for s in g.getSubGraphs():
            dfs(s, cur_height + 1)
            metanodes[s.getId()]['parent_metanode'] = g.getId()

        # Add the field parent_metanode to the leaf node
        for leaf in g.getNodes():
            tmp = leaf_nodes[leaf.id]
            if tmp['parent_metanode'] is None:
                tmp['parent_metanode'] = g.getId()

        # Compute convex hull of this sub-graph in post-order
        if bounding_shape == 'convex_hull':
            if g.numberOfSubGraphs() == 0:
                # compute a convex hull of its leaf nodes
                coords = tlp.computeConvexHull(g)
                node['geometry'] = Polygon([(c.x(), c.y()) for c in coords])
            else:
                # union the convex hull of its sub-graphs
                node['geometry'] = MultiPolygon([
                    metanodes[s.getId()]['geometry'] for s in g.getSubGraphs()
                ]).convex_hull

            # Note that we don't compute the real diameter for a polygon, but instead, only use the diagonal of
            # the axis aligned bounding box to approximate the diameter, which is cheap to compute
            bbox = node['geometry'].bounds
            node['diameter'] = Point(bbox[0],
                                     bbox[1]).distance(Point(bbox[2], bbox[3]))
        elif bounding_shape == 'circle':
            center, fur = tlp.computeBoundingRadius(g)
            radius = center.dist(fur)
            node['geometry'] = Point(center.x(), center.y()).buffer(
                radius, cap_style=CAP_STYLE.round)
            node['diameter'] = 2 * radius
        else:
            bbox = tlp.computeBoundingBox(g)
            node['geometry'] = Polygon([(c.x(), c.y()) for c in bbox])
            node['diameter'] = bbox[0].dist(bbox[1])

        metanodes[g.getId()] = node

    if not leaf_only:
        dfs(graph, root)

    # Output json file at the same directory with same filename but "json" extension
    output_path = re.sub(r'\.tlp$', '.json', input_path)

    # Use the mapping function from shapely to serialize the geometry objects
    for n in leaf_nodes:
        n['geometry'] = mapping(n['geometry'])
    for e in edges:
        e['geometry'] = mapping(e['geometry'])
    for _, n in metanodes.items():
        n['geometry'] = mapping(n['geometry'])

    json_data = {
        'leaf_nodes': leaf_nodes,
        'edges': edges,
        'height': height['a'],
        'root': root,
        'metanodes': metanodes,
        'bounding_box': [[bbox[0].x(), bbox[0].y()],
                         [bbox[1].x(), bbox[1].y()]]
    }
    json.dump(json_data, open(output_path, 'w'))
    print('Converted to ', output_path, ' #nodes:', len(leaf_nodes),
          ' #edges: ', len(edges), ' height: ', height['a'])