1
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
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
56 """
57 Constructor
58 """
59
60 pass
61
62
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
119
120
121
122
123
124
125 if resource.prefix == "gis" and resource.name == "location":
126
127 list_fields = ["wkt"]
128
129 else:
130
131 list_fields = ["location_id$wkt"]
132
133
134 current.s3db.gis_location.wkt.represent = None
135
136
137 (_title, types, lfields, headers, items) = self.extractResource(resource,
138 list_fields)
139
140
141 wkt = items[0]["gis_location.wkt"]
142 if not wkt:
143 current.log.error("No Geometry!")
144
145
146 title = attr.get("title", resource._ids[0])
147 filename = "%s.svg" % title
148 filepath = self.write_file(filename, wkt, **attr)
149
150
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
163
164 from xml.etree import ElementTree as et
165
166
167
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
175 from shapely.wkt import loads as wkt_loads
176 try:
177
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
192 from shapely import affinity
193 bounds = shape.bounds
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
199 shape = affinity.scale(shape, xfact=multiplier, yfact=-multiplier, origin="centroid")
200
201
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
212
213
214
215
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
227
228
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
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
239
240
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
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
268 raise NotImplementedError
269
270 return root
271
272
273