Package s3 :: Package codecs :: Module svg
[frames] | no frames]

Source Code for Module s3.codecs.svg

  1  # -*- coding: utf-8 -*- 
  2   
  3  """ 
  4      S3 SVG codec 
  5   
  6      @copyright: 2013-2019 (c) Sahana Software Foundation 
  7      @license: MIT 
  8   
  9      Permission is hereby granted, free of charge, to any person 
 10      obtaining a copy of this software and associated documentation 
 11      files (the "Software"), to deal in the Software without 
 12      restriction, including without limitation the rights to use, 
 13      copy, modify, merge, publish, distribute, sublicense, and/or sell 
 14      copies of the Software, and to permit persons to whom the 
 15      Software is furnished to do so, subject to the following 
 16      conditions: 
 17   
 18      The above copyright notice and this permission notice shall be 
 19      included in all copies or substantial portions of the Software. 
 20   
 21      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 22      EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 23      OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 24      NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 25      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 26      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 27      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 28      OTHER DEALINGS IN THE SOFTWARE. 
 29  """ 
 30   
 31  __all__ = ("S3SVG",) 
 32   
 33  import os 
 34   
 35  try: 
 36      from cStringIO import StringIO    # Faster, where available 
 37  except: 
 38      from StringIO import StringIO 
 39   
 40  from gluon import * 
 41  from gluon.contenttype import contenttype 
 42  from gluon.storage import Storage 
 43  from gluon.streamer import DEFAULT_CHUNK_SIZE 
 44   
 45  from ..s3codec import S3Codec 
 46  from ..s3utils import s3_unicode, s3_strip_markup 
47 48 # ============================================================================= 49 -class S3SVG(S3Codec):
50 """ 51 Simple SVG format codec 52 """ 53 54 # -------------------------------------------------------------------------
55 - def __init__(self):
56 """ 57 Constructor 58 """ 59 60 pass
61 62 # -------------------------------------------------------------------------
63 - def extractResource(self, resource, list_fields):
64 """ 65 Extract the items from the resource 66 67 @param resource: the resource 68 @param list_fields: fields to include in list views 69 """ 70 71 title = self.crud_string(resource.tablename, "title_list") 72 73 get_vars = Storage(current.request.get_vars) 74 get_vars["iColumns"] = len(list_fields) 75 query, orderby, left = resource.datatable_filter(list_fields, get_vars) 76 resource.add_filter(query) 77 78 data = resource.select(list_fields, 79 left=left, 80 limit=None, 81 orderby=orderby, 82 represent=True, 83 show_links=False) 84 85 rfields = data["rfields"] 86 types = [] 87 colnames = [] 88 heading = {} 89 for rfield in rfields: 90 if rfield.show: 91 colnames.append(rfield.colname) 92 heading[rfield.colname] = rfield.label 93 if rfield.virtual: 94 types.append("string") 95 else: 96 types.append(rfield.ftype) 97 98 items = data["rows"] 99 100 return (title, types, colnames, heading, items)
101 102 # -------------------------------------------------------------------------
103 - def encode(self, resource, **attr):
104 """ 105 Export data as a Scalable Vector Graphic 106 107 @param resource: the source of the data that is to be encoded 108 as an SVG. This may be: 109 resource: the resource 110 item: a list of pre-fetched values 111 the headings are in the first row 112 the data types are in the second row 113 @param attr: dictionary of parameters: 114 * title: The export filename 115 * list_fields: Fields to include in list views 116 """ 117 118 # Get the attributes 119 #list_fields = attr.get("list_fields") 120 #if not list_fields: 121 # list_fields = resource.list_fields() 122 123 # @ToDo: PostGIS can extract SVG from DB (like GeoJSON) 124 # http://postgis.refractions.net/documentation/manual-1.4/ST_AsSVG.html 125 if resource.prefix == "gis" and resource.name == "location": 126 #list_fields.append("wkt") 127 list_fields = ["wkt"] 128 #elif "location_id$wkt" not in list_fields: 129 else: 130 #list_fields.append("location_id$wkt") 131 list_fields = ["location_id$wkt"] 132 133 # Clear the WKT represent 134 current.s3db.gis_location.wkt.represent = None 135 136 # Extract the data from the resource 137 (_title, types, lfields, headers, items) = self.extractResource(resource, 138 list_fields) 139 140 # @ToDo: Support multiple records 141 wkt = items[0]["gis_location.wkt"] 142 if not wkt: 143 current.log.error("No Geometry!") 144 145 # Convert to SVG 146 title = attr.get("title", resource._ids[0]) 147 filename = "%s.svg" % title 148 filepath = self.write_file(filename, wkt, **attr) 149 150 # Response headers 151 disposition = "attachment; filename=\"%s\"" % filename 152 response = current.response 153 response.headers["Content-Type"] = contenttype(".svg") 154 response.headers["Content-disposition"] = disposition 155 156 stream = open(filepath) 157 return response.stream(stream, chunk_size=DEFAULT_CHUNK_SIZE, 158 request=current.request)
159 160 # ------------------------------------------------------------------------- 161 @staticmethod
162 - def write_file(filename, wkt, **attr):
163 164 from xml.etree import ElementTree as et 165 166 # Create an SVG XML element 167 # @ToDo: Allow customisation of height/width 168 iheight = 74 169 height = str(iheight) 170 iwidth = 74 171 width = str(iwidth) 172 doc = et.Element("svg", width=width, height=height, version="1.1", xmlns="http://www.w3.org/2000/svg") 173 174 # Convert WKT 175 from shapely.wkt import loads as wkt_loads 176 try: 177 # Enable C-based speedups available from 1.2.10+ 178 from shapely import speedups 179 speedups.enable() 180 except: 181 current.log.info("S3GIS", 182 "Upgrade Shapely for Performance enhancements") 183 184 shape = wkt_loads(wkt) 185 186 geom_type = shape.geom_type 187 if geom_type not in ("MultiPolygon", "Polygon"): 188 current.log.error("Unsupported Geometry", geom_type) 189 return 190 191 # Scale Points & invert Y axis 192 from shapely import affinity 193 bounds = shape.bounds # (minx, miny, maxx, maxy) 194 swidth = abs(bounds[2] - bounds[0]) 195 sheight = abs(bounds[3] - bounds[1]) 196 width_multiplier = iwidth / swidth 197 height_multiplier = iheight / sheight 198 multiplier = min(width_multiplier, height_multiplier) * 0.9 # Padding 199 shape = affinity.scale(shape, xfact=multiplier, yfact=-multiplier, origin="centroid") 200 201 # Center Shape 202 centroid = shape.centroid 203 xoff = (iwidth / 2) - centroid.x 204 yoff = (iheight / 2) - centroid.y 205 shape = affinity.translate(shape, xoff=xoff, yoff=yoff) 206 207 if geom_type == "MultiPolygon": 208 polygons = shape.geoms 209 elif geom_type == "Polygon": 210 polygons = [shape] 211 # @ToDo: 212 #elif geom_type == "LineString": 213 # _points = shape 214 #elif geom_type == "Point": 215 # _points = [shape] 216 217 points = [] 218 pappend = points.append 219 for polygon in polygons: 220 _points = polygon.exterior.coords 221 for point in _points: 222 pappend("%s,%s" % (point[0], point[1])) 223 224 points = " ".join(points) 225 226 # Wrap in Square for Icon 227 # @ToDo: Anti-Aliased Rounded Corners 228 # @ToDo: Make optional 229 fill = "rgb(167, 192, 210)" 230 stroke = "rgb(114, 129, 145)" 231 et.SubElement(doc, "rect", width=width, height=height, fill=fill, stroke=stroke) 232 233 # @ToDo: Allow customisation of options 234 fill = "rgb(225, 225, 225)" 235 stroke = "rgb(165, 165, 165)" 236 et.SubElement(doc, "polygon", points=points, fill=fill, stroke=stroke) 237 238 # @ToDo: Add Attributes from list_fields 239 240 # Write out File 241 path = os.path.join(current.request.folder, "static", "cache", "svg") 242 if not os.path.exists(path): 243 os.makedirs(path) 244 filepath = os.path.join(path, filename) 245 with open(filepath, "w") as f: 246 # ElementTree 1.2 doesn't write the SVG file header errata, so do that manually 247 f.write("<?xml version=\"1.0\" standalone=\"no\"?>\n") 248 f.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n") 249 f.write("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n") 250 f.write(et.tostring(doc)) 251 252 return filepath
253 254 # -------------------------------------------------------------------------
255 - def decode(self, resource, source, **attr):
256 """ 257 Import data from a Scalable Vector Graphic 258 259 @param resource: the S3Resource 260 @param source: the source 261 262 @return: an S3XML ElementTree 263 264 @ToDo: Handle encodings within SVG other than UTF-8 265 """ 266 267 # @ToDo: Complete this! 268 raise NotImplementedError 269 270 return root
271 272 # End ========================================================================= 273