def to_svg(geometries, envelope=None, max_width=300, max_height=300): svg_top = '<svg xmlns="http://www.w3.org/2000/svg" ' \ 'xmlns:xlink="http://www.w3.org/1999/xlink" ' if len(geometries) == 0: return svg_top + '/>' else: geometry = GeometryCollection(geometries) # Establish SVG canvas that will fit all the data + small space xmin, ymin, xmax, ymax = geometry.bounds if xmin == xmax and ymin == ymax: # This is a point; buffer using an arbitrary size xmin, ymin, xmax, ymax = geometry.buffer(1).bounds else: # Expand bounds by a fraction of the data ranges expand = 0.04 # or 4%, same as R plots widest_part = max([xmax - xmin, ymax - ymin]) expand_amount = widest_part * expand xmin -= expand_amount ymin -= expand_amount xmax += expand_amount ymax += expand_amount dx = xmax - xmin dy = ymax - ymin width = min([max([100., dx]), max_width]) height = min([max([100., dy]), max_height]) try: scale_factor = max([dx, dy]) / max([width, height]) except ZeroDivisionError: scale_factor = 1. view_box, transform = None, None if envelope: xxmin, yymin, xxmax, yymax = envelope.bounds view_box = "{} {} {} {}".format(xxmin, yymin, xxmax - xxmin, yymax - yymin) transform = "matrix(1,0,0,-1,0,{})".format(yymax + yymin) else: view_box = "{} {} {} {}".format(xmin, ymin, dx, dy) transform = "matrix(1,0,0,-1,0,{})".format(ymax + ymin) l = [] for g in geometries: svg = g.svg(scale_factor) if hasattr(g, 'plot'): xml = ElementTree.fromstring(svg) for v in g.plot: xml.set(v, g.plot[v]) svg = ElementTree.tostring(xml).decode() l.append(svg) body = '<g>' + ''.join(l) + '</g>' return ('{0} width="{2}" height="{3}" viewBox="{1}" ' 'preserveAspectRatio="xMidYMid meet">' '<g transform="{4}">{5}</g></svg>').format( svg_top, view_box, width, height, transform, body)