示例#1
0
def extent_handler(node: xml.Element, ctx: Context) -> svgwrite.shapes.Rect:
    """
    handler to transform Proteus Extent object to SVG Circle object
    :param node: XML node with Proteus extent definition
    :param ctx: Proteus model context
    :return: SVG Rect object
    """
    ensure_type(node, 'Extent')
    stroke_width = 0.1
    x_min_str, y_min_str = itemgetter('X', 'Y')(node.find('Min').attrib)
    x_max_str, y_max_str = itemgetter('X', 'Y')(node.find('Max').attrib)

    x_min_f, y_min_f, x_max_f, y_max_f = map(
        float, (x_min_str, y_min_str, x_max_str, y_max_str))
    y_min_f = ctx.y_max - y_min_f
    y_max_f = ctx.y_max - y_max_f
    rect_width = x_max_f - x_min_f
    rect_height = y_min_f - y_max_f
    return ctx.drawing.rect(
        (x_min_f, y_min_f - rect_height), (rect_width, rect_height),
        stroke='red',
        fill='none',
        style=f'stroke-width:{stroke_width}',
        onmouseover='evt.target.setAttribute("fill", "blue")',
        onmouseout='evt.target.setAttribute("fill", "none")')
示例#2
0
def circle_handler(node: xml.Element, ctx: Context) -> svgwrite.shapes.Circle:
    """
    handler to transform Proteus Circle object to SVG Circle object
    :param node: XML node with Proteus circle definition
    :param ctx: Proteus model context
    :return: SVG Circle object
    """
    ensure_type(node, 'Circle')
    presentation_obj = node.find("Presentation")

    if presentation_obj is None:
        raise AssertionError(
            f'"Presentation" node expected but not found in Line node')

    stroke_color = fetch_color_from_presentation(presentation_obj)
    stroke_width = float(
        presentation_obj.attrib.get('LineWeight')) * ctx.units.value
    radius = float(node.attrib.get('Radius')) * ctx.units.value
    is_filled = True if node.attrib.get('Filled') else False
    coordinates = node.find('Position').find('Location')
    x_pos, y_pos = map(lambda x: float(x) * ctx.units.value,
                       itemgetter('X', 'Y')(coordinates.attrib))

    circle = svgwrite.shapes.Circle(
        (x_pos, ctx.y_max - y_pos),
        radius,
        fill='none' if not is_filled else stroke_color,
        stroke=stroke_color,
        style=f'stroke-width:{stroke_width}')

    return circle
示例#3
0
def pipe_connector_symbol_handler(node: xml._Element,
                                  ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Proteus PipeConnectorSymbol node. This is a complex node which contains other nodes
    :param node:
    :param ctx:
    :return:
    """
    ensure_type(node, 'PipeConnectorSymbol')

    pipe_conn_group = create_group(ctx, node)

    cross_page_conn = node.find('CrossPageConnection')
    if cross_page_conn is not None:
        pipe_conn_group.attribs[
            'data-drawing-name'] = cross_page_conn.attrib.get('DrawingName')
        pipe_conn_group.attribs[
            'data-drawing-link-label'] = cross_page_conn.attrib.get(
                'LinkLabel')
        # todo we could also need support for CrossPageConnection with linkedPersistentId

    if node.attrib.get(ATTR_COMP_NAME) is not None:
        shape_reference = ctx.get_from_shape_catalog(
            'PipeConnectorSymbol', node.attrib[ATTR_COMP_NAME])
        process_shape_reference(node, shape_reference, ctx)

    return pipe_conn_group
示例#4
0
def fetch_color_from_presentation(pr_obj: XMLParse.Element) -> str:
    """
    function to fetch color from Presentation object
    :param pr_obj: Presentation object with 'R', 'G','B' objects defined
    :return: color as hex string
    """
    ensure_type(pr_obj, 'Presentation')
    red, green, blue = map(float, itemgetter('R', 'G', 'B')(pr_obj.attrib))
    return rgb_to_hex_string(*compute_rgb(red, green, blue))
示例#5
0
def drawing_handler(node: xml.Element,
                    ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Proteus Drawing node. This is a complex node which contains other nodes such as Label, Text, etc.
    :param node: Drawing to process
    :param ctx: drawing context
    :return: SVG group object where other drawing parts will be added later
    """
    ensure_type(node, 'Drawing')
    return create_group(ctx, node)
示例#6
0
def piping_network_segment_handler(node: xml._Element,
                                   ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Proteus PipingNetworkSegment node. This is a complex node which contains other nodes
    :param node:
    :param ctx:
    :return:
    """
    ensure_type(node, 'PipingNetworkSegment')
    pn_sys_group = create_group(ctx, node)
    return pn_sys_group
示例#7
0
 def process_circle(circle_node: xml.Element):
     ensure_type(circle_node, 'Circle')
     _presentation_obj = circle_node.find('Presentation')
     _radius = float(circle_node.attrib.get('Radius')) * ctx.units.value
     _x, _y = map(
         lambda val: float(val) * ctx.units.value,
         itemgetter('X', 'Y')(
             circle_node.find('Position').find('Location').attrib))
     _stroke_color = fetch_color_from_presentation(_presentation_obj)
     _stroke_width = float(
         _presentation_obj.attrib.get('LineWeight')) * ctx.units.value
     return _x, _y, _radius, _stroke_color, _stroke_width
示例#8
0
def instrument_component_handler(node: xml._Element, ctx: Context):
    """
    Handler to process Proteus InstrumentComponent node. This is a complex node which contains other nodes
    and can be referenced in ShapeCatalogue
    :param node:
    :param ctx:
    :return:
    """
    ensure_type(node, 'InstrumentComponent')
    pipe_comp_group = create_group(ctx, node)
    shape_reference = ctx.get_from_shape_catalog(
        'InstrumentComponent', node.attrib.get(ATTR_COMP_NAME))
    process_shape_reference(node, shape_reference, ctx)
    return pipe_comp_group
示例#9
0
def component_handler(node: xml._Element,
                      ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Proteus Component node. This is a complex node which contains other nodes
    :param node:
    :param ctx:
    :return:
    """
    ensure_type(node, 'Component')
    pn_sys_group = create_group(ctx, node)
    shape_reference = ctx.get_from_shape_catalog(
        'Component', node.attrib.get(ATTR_COMP_NAME))
    process_shape_reference(node, shape_reference, ctx)
    return pn_sys_group
示例#10
0
def process_instrumentation_function_handler(node: xml._Element, ctx: Context):
    """
    Handler to process Proteus ProcessInstrumentationFunction node. This is a complex node which contains other nodes
    and can be referenced in ShapeCatalogue
    :param node:
    :param ctx:
    :return:
    """
    ensure_type(node, 'ProcessInstrumentationFunction')

    shape_reference = ctx.get_from_shape_catalog(
        'ProcessInstrumentationFunction', node.attrib.get(ATTR_COMP_NAME))
    process_shape_reference(node, shape_reference, ctx)

    return create_group(ctx, node)
示例#11
0
def piping_component_handler(node: xml._Element,
                             ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Proteus PipingComponent node. This is a complex node which contains other nodes
    and can be referenced in ShapeCatalogue
    :param node: node to process
    :param ctx: drawing context
    :return: svgwrite.container.Group object
    """
    ensure_type(node, 'PipingComponent')
    pipe_comp_group = create_group(ctx, node)
    shape_reference = ctx.get_from_shape_catalog(
        'PipingComponent', node.attrib.get(ATTR_COMP_NAME))
    process_shape_reference(node, shape_reference, ctx)
    return pipe_comp_group
示例#12
0
def label_handler(node: xml._Element,
                  ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Proteus Label node. This is a complex node which contains other nodes
    and can be referenced in ShapeCatalogue
    :param node:
    :param ctx:
    :return:
    """
    ensure_type(node, 'Label')

    shape_reference = ctx.get_from_shape_catalog(
        'Label', node.attrib.get(ATTR_COMP_NAME))
    process_shape_reference(node, shape_reference, ctx)

    return create_group(ctx, node)
示例#13
0
def nozzle_handler(node: xml._Element,
                   ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Nozzle object
    :param node: object to process
    :param ctx: drawing context
    :return: SVG group object
    """
    ensure_type(node, 'Nozzle')

    nozzle_group = create_group(ctx, node)

    shape_reference = ctx.get_from_shape_catalog(
        'Nozzle', nozzle_group.attribs[DATA_COMPONENT_NAME])
    process_shape_reference(node, shape_reference, ctx)
    return nozzle_group
示例#14
0
def shape_handler(node: xml.Element, ctx: Context) -> svgwrite.path.Path:
    """
    handler to transform Proteus PolyLine geometric curve primitive to SVG path object
    :param node: XML node with Proteus CenterLine definition
    :param ctx: drawing context
    :return: XML object with SVG path definition
    """
    ensure_type(node, 'Shape')

    presentation_obj = node.find("Presentation")

    # as of P&ID profile file specification - The drawing behaviour is undefined if the Presentation element is missing.
    if presentation_obj is None:
        parent_presentation_obj = node.getparent().find('Presentation')
        stroke_color = fetch_color_from_presentation(parent_presentation_obj)
        stroke_width = float(
            parent_presentation_obj.attrib.get('LineWeight')) * ctx.units.value
    else:
        stroke_color = fetch_color_from_presentation(presentation_obj)
        stroke_width = float(
            presentation_obj.attrib.get('LineWeight')) * ctx.units.value

    coordinates = node.findall('Coordinate')

    is_filled = True if node.attrib.get('Filled') else False

    path = svgwrite.path.Path(None,
                              stroke=stroke_color,
                              fill='none' if not is_filled else stroke_color,
                              style=f'stroke-width:{stroke_width}')

    start_point = True
    for coordinate in coordinates:
        operation = 'M' if start_point else 'L'
        start_point = False
        x_, y_ = map(lambda x: float(x) * ctx.units.value,
                     itemgetter('X', 'Y')(coordinate.attrib))
        path.push(operation, x_, ctx.y_max - y_)

    line_type = fetch_line_type_from_presentation(presentation_obj)
    if line_type:
        path.dasharray(line_type)

    return path.push('Z')
示例#15
0
def line_handler(node: xml.Element, ctx: Context) -> svgwrite.shapes.Line:
    """
    handler to transform Proteus line object to SVG line object
    :param node: XML node with Proteus line definition
    :param ctx: Proteus model context
    :return: SVG line object
    """
    ensure_type(node, 'Line')
    coordinates = node.findall('Coordinate')

    if len(coordinates) != 2:
        raise AssertionError(
            f'a line must have a pair of coordinates, {len(coordinates)} found'
        )

    presentation_obj = node.find("Presentation")

    if presentation_obj is None:
        raise AssertionError(
            f'"Presentation" node expected but not found in Line node')

    stroke_color = fetch_color_from_presentation(presentation_obj)
    stroke_width = float(
        presentation_obj.attrib.get('LineWeight')) * ctx.units.value

    x_min_str, y_min_str = itemgetter('X', 'Y')(coordinates[0].attrib)
    x_max_str, y_max_str = itemgetter('X', 'Y')(coordinates[1].attrib)

    x_min_f, y_min_f, x_max_f, y_max_f = map(
        lambda x: float(x) * ctx.units.value,
        (x_min_str, y_min_str, x_max_str, y_max_str))

    line = svgwrite.shapes.Line(start=(x_min_f, ctx.y_max - y_min_f),
                                end=(x_max_f, ctx.y_max - y_max_f),
                                stroke=stroke_color,
                                stroke_width=stroke_width)

    line_type = fetch_line_type_from_presentation(presentation_obj)
    if line_type:
        line.dasharray(line_type)
    return line
示例#16
0
def fetch_line_type_from_presentation(pr_obj: xml._Element,
                                      fallback='Solid') -> list:
    """
    function to fetch and convert Proteus line type to SVG stroke-dasharray
    One of the numbers or names from the following (Object Model document v2.2) :-
    0 Solid
    1 Dotted
    2 Dashed
    3 Long Dash
    4 Long Dash + Short Dash, CenterLine
    5 Short Dash
    6 Long Dash + Short Dash + Short Dash
    7 Dash + Short Dash
    :param fallback: default line type
    :param pr_obj: Presentation object
    :return: list values for stroke-dasharray SVG property
    """
    ensure_type(pr_obj, 'Presentation')
    line_type = pr_obj.attrib.get('LineType', fallback).lower()
    if '0' == line_type or 'solid' == line_type:
        return []
    if '1' == line_type or 'dotted' == line_type:
        return [1]
    if '2' == line_type or 'dashed' == line_type:
        return [3]
    if '3' == line_type or 'long dash' == line_type:
        return [4, 1]
    if '4' == line_type or 'long dash + short dash, centerline' == line_type:
        return [4, 1, 2, 1]
    if '5' == line_type or 'short dash' == line_type:
        return [2, 1]
    if '6' == line_type or 'long dash + short dash + short dash' == line_type:
        return [4, 1, 2, 1, 2, 1]
    if '7' == line_type or 'dash + short dash' == line_type:
        return [3, 1, 2, 1]
    raise ValueError(f'unknown line type ({line_type}) found')
示例#17
0
def equipment_handler(node: xml._Element,
                      ctx: Context) -> svgwrite.container.Group:
    """
    Handler to process Equipment object
    :param node: node to process
    :param ctx: model context
    :return: SVG group object where Equipment parts will be added later
    """
    ensure_type(node, 'Equipment')

    eq_group = create_group(ctx, node)

    # fixme this one will work with Comos but what if we get drawing from any other systems?
    # eq_group.attribs[DATA_LABEL] = get_gen_attr_val(node, 'ComosProperties', 'Label')
    # eq_group.attribs[DATA_FULL_LABEL] = get_gen_attr_val(node, 'ComosProperties', 'FullLabel')

    # this it temporary fix for that
    # attributes_to_add_from_origin method should be updated if we get new Proteus data sources
    # probably we could move it into external config file instead of code
    # probably we also need common data-* name's which do not depend on originating system
    for attr in ctx.attributes_to_add_from_origin():
        for attr_value in attr.get('values'):
            key = f'data-{ctx.origin.lower()}-{attr_value.lower()}'
            value = get_gen_attr_val(node, attr['set'], attr_value)
            if value is not None:
                eq_group.attribs[key] = value

    descr_obj = node.find('Description')
    if descr_obj is not None:
        eq_group.attribs['data-description'] = descr_obj.text

    if eq_group.attribs.get(DATA_COMPONENT_NAME) is not None:
        shape_reference = ctx.get_from_shape_catalog(
            'Equipment', eq_group.attribs[DATA_COMPONENT_NAME])
        process_shape_reference(node, shape_reference, ctx)
    return eq_group
示例#18
0
def text_handler(node: xml.Element, ctx: Context) -> svgwrite.text.Text:
    """
    handler to transform Proteus Text object to SVG String object
    :param node: XML node with Proteus line definition
    :param ctx: Proteus model context
    :return: SVG line object
    """
    ensure_type(node, 'Text')

    if node.attrib.get('String'):
        text_arr = re.split(LINEBREAK_PATTERN, node.attrib.get('String'))
    else:
        # todo add support for displaying values stored in generic attribute
        # see 4.2.3.1 of DEXPI 1.2 spec
        return
    text_font = node.attrib.get('Font')
    text_size = round(float(node.attrib.get('Height')) * ctx.units.value)

    text_color = DEFAULT_STROKE_COLOR
    if node.find('Presentation') is not None:
        text_color = fetch_color_from_presentation(node.find('Presentation'))

    style = f'font-size:{text_size}px; font-family:{text_font}; fill:{text_color}'

    text_angle = float(node.attrib.get('TextAngle')) if node.attrib.get(
        'TextAngle') is not None else 0

    text_justification = node.attrib.get('Justification')
    if not text_justification:
        text_justification = 'LeftBottom'

    if text_justification.startswith('Left'):
        svg_jst = 'start'
    elif text_justification.startswith('Right'):
        svg_jst = 'end'
    elif text_justification.startswith('Center'):
        svg_jst = 'middle'
    else:
        raise ValueError(f'Unknown justification {text_justification}')

    if text_justification.endswith('Top'):
        alignment_baseline = 'baseline'
    elif text_justification.endswith('Center'):
        alignment_baseline = 'middle'
    elif text_justification.endswith('Bottom'):
        alignment_baseline = 'hanging'
    else:
        raise ValueError(f'Unknown justification {text_justification}')

    text_pos = node.find('Position')
    if text_pos is None:
        text_pos = node.getparent().find('Position')

    text_pos_x, text_pos_y = map(
        lambda x: float(x) * ctx.units.value,
        itemgetter('X', 'Y')(text_pos.find('Location').attrib))

    text_obj = svgwrite.text.Text(text_arr[0],
                                  x=[text_pos_x],
                                  y=[ctx.y_max - text_pos_y - text_size / 2],
                                  style=style,
                                  text_anchor=svg_jst,
                                  alignment_baseline=alignment_baseline)
    text_obj.rotate(-text_angle, (text_pos_x, -(text_pos_y - ctx.y_max)))

    for span in text_arr[1:]:
        t_span = svgwrite.text.TSpan(span,
                                     x=[float(text_pos_x)],
                                     dy=[text_size],
                                     text_anchor=svg_jst,
                                     alignment_baseline=alignment_baseline)
        text_obj.add(t_span)

    return text_obj