Source code for svg_model.detect_connections

# coding: utf-8
import types

from lxml import etree

from .connections import extract_adjacent_shapes
from .draw import draw_lines_svg_layer
from . import INKSCAPE_NSMAP, compute_shape_centers, svg_shapes_to_df


[docs]def auto_detect_adjacent_shapes(svg_source, shape_i_attr='id', layer_name='Connections', shapes_xpath='//svg:path | //svg:polygon', extend=1.5): ''' Attempt to automatically find "adjacent" shapes in a SVG layer. In a layer within a new SVG document, draw each detected connection between the center points of the corresponding shapes. Parameters ---------- svg_source : str Input SVG file as a filepath (or file-like object). shape_i_attr : str, optional Attribute of each shape SVG element that uniquely identifies the shape. layer_name : str, optional Name to use for the output layer where detected connections are drawn. .. note:: Any existing layer with the same name will be overwritten. shapes_xpath : str, optional XPath path expression to select shape nodes. By default, all ``svg:path`` and ``svg:polygon`` elements are selected. extend : float, optional Extend ``x``/``y`` coords by the specified number of absolute units from the center point of each shape. Each shape is stretched independently in the ``x`` and ``y`` direction. In each direction, a shape is considered adjacent to all other shapes that are overlapped by the extended shape. Returns ------- StringIO.StringIO File-like object containing SVG document with layer named according to :data:`layer_name` with the detected connections drawn as ``svg:line`` instances. ''' # Read SVG polygons into dataframe, one row per polygon vertex. df_shapes = svg_shapes_to_df(svg_source, xpath=shapes_xpath) df_shapes = compute_shape_centers(df_shapes, shape_i_attr) df_shape_connections = extract_adjacent_shapes(df_shapes, shape_i_attr, extend=extend) # Parse input file. xml_root = etree.parse(svg_source) svg_root = xml_root.xpath('/svg:svg', namespaces=INKSCAPE_NSMAP)[0] # Get the center coordinate of each shape. df_shape_centers = (df_shapes.drop_duplicates(subset=[shape_i_attr]) [[shape_i_attr] + ['x_center', 'y_center']] .set_index(shape_i_attr)) # Get the center coordinate of the shapes corresponding to the two # endpoints of each connection. df_connection_centers = (df_shape_centers.loc[df_shape_connections.source] .reset_index(drop=True) .join(df_shape_centers.loc[df_shape_connections .target] .reset_index(drop=True), lsuffix='_source', rsuffix='_target')) # Remove existing connections layer from source, in-memory XML (source file # remains unmodified). A new connections layer will be added below. connections_xpath = '//svg:g[@inkscape:label="%s"]' % layer_name connections_groups = svg_root.xpath(connections_xpath, namespaces=INKSCAPE_NSMAP) if connections_groups: for g in connections_groups: g.getparent().remove(g) # Create in-memory SVG svg_output = \ draw_lines_svg_layer(df_connection_centers .rename(columns={'x_center_source': 'x_source', 'y_center_source': 'y_source', 'x_center_target': 'x_target', 'y_center_target': 'y_target'}), layer_name=layer_name) return svg_output