[Matplotlib-users] Arbitrary artist data on SVG elements

Thomas Caswell tcaswell at gmail.com
Sat Apr 1 16:30:30 EDT 2017


Joshua,

That is an interesting use case!

I am hesitant to add this attribute to Artist because it is very specific
to the SVG backend (none of the other backends would make use of this as
far as I know). On the other hand, a generic way to use gid to add extra
information in the SVG backend could be interesting.  I am pretty sure
there are examples of optianal backend-specific kwargs going into
`savefig`, what would the API for that look like?

Tom

On Wed, Mar 22, 2017 at 4:42 PM Joshua Klein <mobiusklein at gmail.com> wrote:

> Hello,
>
> I often embed figures as SVG graphics in web pages. As part of this
> process, I usually do the following
>
>    1.
>
>    Set gids on artists and link that gid to a set of data describing that
>    part of a graphic in an external dictionary. This includes things like
>    setting the element’s class, extra contextual information, information that
>    would be good to show in a tooltip, ids of related elements, and so forth.
>    2.
>
>    Serialize the figure into a file-like object, use an element tree
>    implementation’s XMLID to get an element id map and Element objects
>    3.
>
>    Iterate over my data dictionary from (1) and set keys in the mapped
>    Element’s attrib dictionary, using the id map from (2)
>    4.
>
>    Use the element tree implementation’s tostring function to serialize
>    the updated Element objects back into a string and then send the string out
>    as a response to a web request.
>    5.
>
>    After receiving the SVG string from the server on the client, add the
>    SVG to the page’s DOM and then hang event handlers on it (or pre-specify
>    delegated handlers) that use the added attributes to configure interactive
>    behavior.
>
> I looked at the Artist type and saw no good place to store “arbitrary
> data”. Before I start working on this I wanted to know if anyone else had a
> better solution. I would also like to know if the devs would be opposed to
> a PR that adds an extra dictionary/attribute to every Artist instance
> created.
>
> Another alternative solution would be to find a way to push my dictionary
> mapping gids to extra attributes into the SVGRenderer and have it pass
> them as **extras to XMLWriter.element when it processes individual
> artists.
>
> Here’s a generic example of what I do currently:
>
> def plot_with_extras_for_svg(*data, **kwargs):
>     # Do the plotting, generating the id-linked data in `id_mapper`
>     ax, id_mapper = plot_my_data(*data, **kwargs)
>     xlim = ax.get_xlim()
>     ylim = ax.get_ylim()
>
>     # compute the total space used in both dimensions when dealing with
>     # negative axis bounds
>     x_size = sum(map(abs, xlim))
>     y_size = sum(map(abs, ylim))
>
>     # Map the used axis space to the drawable region dimensions
>     aspect_ratio = x_size / y_size
>     canvas_x = 8.
>     canvas_y = canvas_x / aspect_ratio
>
>     # Configure the artist to draw within the new drawable region bounds
>     fig = ax.get_figure()
>     fig.tight_layout(pad=0.2)
>     fig.patch.set_visible(False)
>     fig.set_figwidth(canvas_x)
>     fig.set_figheight(canvas_y)
>
>     ax.patch.set_visible(False)
>
>     # Perform the first serialization
>     buff = StringIO()
>     fig.savefig(buff, format='svg')
>
>     # Parse XML buffer from `buff` and configure tag attributes
>     root, ids = ET.XMLID(buff.getvalue())
>     root.attrib['class'] = 'plot-class-svg'
>     for id, attributes in id_mapper.items():
>         element = ids[id]
>         element.attrib.update({("data-" + k): str(v)
>                                for k, v in attributes.items()})
>         element.attrib['class'] = id.rsplit('-')[0]
>
>     # More drawable space shenanigans
>     min_x, min_y, max_x, max_y = map(int, root.attrib["viewBox"].split(" "))
>     min_x += 100
>     max_x += 200
>     view_box = ' '.join(map(str, (min_x, min_y, max_x, max_y)))
>     root.attrib["viewBox"] = view_box
>     width = float(root.attrib["width"][:-2]) * 1.75
>     root.attrib["width"] = "100%"
>
>     height = width / (aspect_ratio)
>
>     root.attrib["height"] = "%dpt" % (height * 1.2)
>     root.attrib["preserveAspectRatio"] = "xMinYMin meet"
>
>     # Second serialization
>     svg = ET.tostring(root)
>     plt.close(fig)
>
>     return svg
>
> Thank you
>> _______________________________________________
> Matplotlib-users mailing list
> Matplotlib-users at python.org
> https://mail.python.org/mailman/listinfo/matplotlib-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-users/attachments/20170401/2d693775/attachment-0001.html>


More information about the Matplotlib-users mailing list