Package djsld :: Module generator
[hide private]
[frames] | no frames]

Source Code for Module djsld.generator

  1  """ 
  2  Generate StyledLayerDescriptor objecs for django querysets. 
  3   
  4  This generator uses the python-sld and pysal libraries to generate classes 
  5  for map classification, and returns a StyledLayerDescriptor class object. This 
  6  class object may be serialized to an SLD XML file, which is useful for many 
  7  GIS and mapping software packages. 
  8   
  9  License 
 10  ======= 
 11  Copyright 2011-2012 David Zwarg <U{dzwarg@azavea.com}> 
 12   
 13  Licensed under the Apache License, Version 2.0 (the "License"); 
 14  you may not use this file except in compliance with the License. 
 15  You may obtain a copy of the License at 
 16   
 17  U{http://www.apache.org/licenses/LICENSE-2.0} 
 18   
 19  Unless required by applicable law or agreed to in writing, software 
 20  distributed under the License is distributed on an "AS IS" BASIS, 
 21  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 22  See the License for the specific language governing permissions and 
 23  limitations under the License. 
 24   
 25  @author: David Zwarg 
 26  @contact: dzwarg@azavea.com 
 27  @copyright: 2011-2012, Azavea 
 28  @license: Apache 2.0 
 29  @version: 1.0.7 
 30  """ 
 31   
 32  from sld import * 
 33  from numpy import array, ndarray 
 34  from pysal.esda.mapclassify import * 
 35  from django.contrib.gis.db.models import fields 
 36   
37 -def as_equal_interval(*args, **kwargs):
38 """ 39 Generate equal interval classes from the provided queryset. If the queryset 40 is empty, no class breaks are returned. For more information on the Equal 41 Interval classifier, please visit: 42 43 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Equal_Interval} 44 45 @type queryset: QuerySet 46 @param queryset: The query set that contains the entire distribution of 47 data values. 48 @type field: string 49 @param field: The name of the field on the model in the queryset that 50 contains the data values. 51 @type nclasses: integer 52 @param nclasses: The number of class breaks desired. 53 @type geofield: string 54 @param geofield: The name of the geometry field. Defaults to 'geom'. 55 @rtype: L{sld.StyledLayerDescriptor} 56 @returns: An SLD object that represents the class breaks. 57 """ 58 return _as_classification(Equal_Interval, *args, **kwargs)
59
60 -def as_fisher_jenks(*args, **kwargs):
61 """ 62 Generate Fisher-Jenks classes from the provided queryset. If the queryset 63 is empty, no class breaks are returned. For more information on the Fisher 64 Jenks classifier, please visit: 65 66 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Fisher_Jenks} 67 68 @type queryset: QuerySet 69 @param queryset: The query set that contains the entire distribution of 70 data values. 71 @type field: string 72 @param field: The name of the field on the model in the queryset that 73 contains the data values. 74 @type nclasses: integer 75 @param nclasses: The number of class breaks desired. 76 @type geofield: string 77 @param geofield: The name of the geometry field. Defaults to 'geom'. 78 @rtype: L{sld.StyledLayerDescriptor} 79 @returns: An SLD object that represents the class breaks. 80 """ 81 return _as_classification(Fisher_Jenks, *args, **kwargs)
82
83 -def as_jenks_caspall(*args, **kwargs):
84 """ 85 Generate Jenks-Caspall classes from the provided queryset. If the queryset 86 is empty, no class breaks are returned. For more information on the Jenks 87 Caspall classifier, please visit: 88 89 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Jenks_Caspall} 90 91 @type queryset: QuerySet 92 @param queryset: The query set that contains the entire distribution of 93 data values. 94 @type field: string 95 @param field: The name of the field on the model in the queryset that 96 contains the data values. 97 @type nclasses: integer 98 @param nclasses: The number of class breaks desired. 99 @type geofield: string 100 @param geofield: The name of the geometry field. Defaults to 'geom'. 101 @rtype: L{sld.StyledLayerDescriptor} 102 @returns: An SLD object that represents the class breaks. 103 """ 104 return _as_classification(Jenks_Caspall, *args, **kwargs)
105
106 -def as_jenks_caspall_forced(*args, **kwargs):
107 """ 108 Generate Jenks-Caspall Forced classes from the provided queryset. If the queryset 109 is empty, no class breaks are returned. For more information on the Jenks 110 Caspall Forced classifier, please visit: 111 112 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Jenks_Caspall_Forced} 113 114 @type queryset: QuerySet 115 @param queryset: The query set that contains the entire distribution of 116 data values. 117 @type field: string 118 @param field: The name of the field on the model in the queryset that 119 contains the data values. 120 @type nclasses: integer 121 @param nclasses: The number of class breaks desired. 122 @type geofield: string 123 @param geofield: The name of the geometry field. Defaults to 'geom'. 124 @rtype: L{sld.StyledLayerDescriptor} 125 @returns: An SLD object that represents the class breaks. 126 """ 127 return _as_classification(Jenks_Caspall_Forced, *args, **kwargs)
128
129 -def as_jenks_caspall_sampled(*args, **kwargs):
130 """ 131 Generate Jenks-Caspall Sampled classes from the provided queryset. If the queryset 132 is empty, no class breaks are returned. For more information on the Jenks 133 Caspall Sampled classifier, please visit: 134 135 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Jenks_Caspall_Sampled} 136 137 @type queryset: QuerySet 138 @param queryset: The query set that contains the entire distribution of 139 data values. 140 @type field: string 141 @param field: The name of the field on the model in the queryset that 142 contains the data values. 143 @type nclasses: integer 144 @param nclasses: The number of class breaks desired. 145 @type geofield: string 146 @param geofield: The name of the geometry field. Defaults to 'geom'. 147 @rtype: L{sld.StyledLayerDescriptor} 148 @returns: An SLD object that represents the class breaks. 149 """ 150 return _as_classification(Jenks_Caspall_Sampled, *args, **kwargs)
151
152 -def as_max_p_classifier(*args, **kwargs):
153 """ 154 Generate Max P classes from the provided queryset. If the queryset 155 is empty, no class breaks are returned. For more information on the Max P 156 classifier, please visit: 157 158 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Max_P_Classifier} 159 160 @type queryset: QuerySet 161 @param queryset: The query set that contains the entire distribution of 162 data values. 163 @type field: string 164 @param field: The name of the field on the model in the queryset that 165 contains the data values. 166 @type nclasses: integer 167 @param nclasses: The number of class breaks desired. 168 @type geofield: string 169 @param geofield: The name of the geometry field. Defaults to 'geom'. 170 @rtype: L{sld.StyledLayerDescriptor} 171 @returns: An SLD object that represents the class breaks. 172 """ 173 return _as_classification(Max_P_Classifier, *args, **kwargs)
174
175 -def as_maximum_breaks(*args, **kwargs):
176 """ 177 Generate Maximum Breaks classes from the provided queryset. If the queryset 178 is empty, no class breaks are returned. For more information on the Maximum 179 Breaks classifier, please visit: 180 181 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Maximum_Breaks} 182 183 @type queryset: QuerySet 184 @param queryset: The query set that contains the entire distribution of 185 data values. 186 @type field: string 187 @param field: The name of the field on the model in the queryset that 188 contains the data values. 189 @type nclasses: integer 190 @param nclasses: The number of class breaks desired. 191 @type geofield: string 192 @param geofield: The name of the geometry field. Defaults to 'geom'. 193 @rtype: L{sld.StyledLayerDescriptor} 194 @returns: An SLD object that represents the class breaks. 195 """ 196 return _as_classification(Maximum_Breaks, *args, **kwargs)
197
198 -def as_natural_breaks(*args, **kwargs):
199 """ 200 Generate Natural Breaks classes from the provided queryset. If the queryset 201 is empty, no class breaks are returned. For more information on the Natural 202 Breaks classifier, please visit: 203 204 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Natural_Breaks} 205 206 @type queryset: QuerySet 207 @param queryset: The query set that contains the entire distribution of 208 data values. 209 @type field: string 210 @param field: The name of the field on the model in the queryset that 211 contains the data values. 212 @type nclasses: integer 213 @param nclasses: The number of class breaks desired. 214 @type geofield: string 215 @param geofield: The name of the geometry field. Defaults to 'geom'. 216 @rtype: L{sld.StyledLayerDescriptor} 217 @returns: An SLD object that represents the class breaks. 218 """ 219 return _as_classification(Natural_Breaks, *args, **kwargs)
220
221 -def as_quantiles(*args, **kwargs):
222 """ 223 Generate Quantile classes from the provided queryset. If the queryset 224 is empty, no class breaks are returned. For more information on the Quantile 225 classifier, please visit: 226 227 U{http://pysal.geodacenter.org/1.2/library/esda/mapclassify.html#pysal.esda.mapclassify.Quantiles} 228 229 @type queryset: QuerySet 230 @param queryset: The query set that contains the entire distribution of 231 data values. 232 @type field: string 233 @param field: The name of the field on the model in the queryset that 234 contains the data values. 235 @type nclasses: integer 236 @param nclasses: The number of class breaks desired. 237 @type geofield: string 238 @param geofield: The name of the geometry field. Defaults to 'geom'. 239 @rtype: L{sld.StyledLayerDescriptor} 240 @returns: An SLD object that represents the class breaks. 241 """ 242 return _as_classification(Quantiles, *args, **kwargs)
243
244 -def _as_classification(classification, queryset, field, nclasses, geofield='geom', 245 propertyname=None, userstyletitle=None, featuretypestylename=None, colorbrewername='', 246 invertgradient=False, **kwargs):
247 """ 248 Accept a queryset of objects, and return the values of the class breaks 249 on the data distribution. If the queryset is empty, no class breaks are 250 computed. 251 252 @type classification: pysal classifier 253 @param classification: A classification class defined in 254 pysal.esda.mapclassify. As of version 1.0.1, this list is comprised of: 255 256 - Equal_Interval 257 - Fisher_Jenks 258 - Jenks_Caspall 259 - Jenks_Caspall_Forced 260 - Jenks_Caspall_Sampled 261 - Max_P_Classifier 262 - Maximum_Breaks 263 - Natural_Breaks 264 - Quantiles 265 266 @type queryset: QuerySet 267 @param queryset: The query set that contains the entire distribution of data values. 268 @type field: string 269 @param field: The name of the field on the model in the queryset that contains the data values. 270 @type nclasses: integer 271 @param nclasses: The number of class breaks desired. 272 @type geofield: string 273 @keyword geofield: The name of the geography column on the model. Defaults to 'geom' 274 @type propertyname: string 275 @keyword propertyname: The name of the filter property name, if different from the model field. 276 @type userstyletitle: string 277 @keyword userstyletitle: The title of the UserStyle element. 278 @type featuretypestylename: string 279 @keyword featuretypestylename: The name of the FeatureTypeStyle element. 280 @type colorbrewername: string 281 @keyword colorbrewername: The name of a colorbrewer ramp name. Must have the same # of corresponding classes as nclasses. 282 @type invertgradient: boolean 283 @keyword invertgradient: Should the resulting SLD have colors from high to low, instead of low to high? 284 @type kwargs: keywords 285 @param kwargs: Additional keyword arguments for the classifier. 286 @rtype: L{sld.StyledLayerDescriptor} 287 @returns: An SLD class object that represents the classification scheme 288 and filters. 289 """ 290 thesld = StyledLayerDescriptor() 291 292 ftype = queryset.model._meta.get_field_by_name(geofield)[0] 293 if isinstance(ftype, fields.LineStringField) or isinstance(ftype, fields.MultiLineStringField): 294 symbolizer = LineSymbolizer 295 elif isinstance(ftype, fields.PolygonField) or isinstance(ftype, fields.MultiPolygonField): 296 symbolizer = PolygonSymbolizer 297 else: 298 # PointField, MultiPointField, GeometryField, or GeometryCollectionField 299 symbolizer = PointSymbolizer 300 301 if propertyname is None: 302 propertyname = field 303 304 nl = thesld.create_namedlayer('%d breaks on "%s" as %s' % (nclasses, field, classification.__name__)) 305 us = nl.create_userstyle() 306 if not userstyletitle is None: 307 us.Title = str(userstyletitle) 308 fts = us.create_featuretypestyle() 309 if not featuretypestylename is None: 310 fts.Name = str(featuretypestylename) 311 312 # with just one class, make a single static style with no filters 313 if nclasses == 1: 314 rule = fts.create_rule(propertyname, symbolizer=symbolizer) 315 shade = 0 if invertgradient else 255 316 shade = '#%02x%02x%02x' % (shade, shade, shade,) 317 318 # no filters for one class 319 if symbolizer == PointSymbolizer: 320 rule.PointSymbolizer.Graphic.Mark.Fill.CssParameters[0].Value = shade 321 elif symbolizer == LineSymbolizer: 322 rule.LineSymbolizer.Stroke.CssParameters[0].Value = shade 323 elif symbolizer == PolygonSymbolizer: 324 rule.PolygonSymbolizer.Stroke.CssParameters[0].Value = '#000000' 325 rule.PolygonSymbolizer.Fill.CssParameters[0].Value = shade 326 327 thesld.normalize() 328 329 return thesld 330 331 # with more than one class, perform classification 332 datavalues = array(queryset.order_by(field).values_list(field, flat=True)) 333 q = classification(datavalues, nclasses, **kwargs) 334 335 shades = None 336 if q.k == nclasses and colorbrewername and not colorbrewername == '': 337 try: 338 import colorbrewer 339 shades = getattr(colorbrewer, colorbrewername)[nclasses] 340 341 if invertgradient: 342 shades.reverse() 343 except: 344 # could not import colorbrewer, or nclasses unavailable 345 pass 346 347 for i,qbin in enumerate(q.bins): 348 if type(qbin) == ndarray: 349 qbin = qbin[0] 350 351 title = '<= %s' % qbin 352 rule = fts.create_rule(title, symbolizer=symbolizer) 353 354 if shades: 355 shade = '#%02x%02x%02x' % shades[i] 356 else: 357 shade = (float(q.k - i) / q.k ) * 255 358 if invertgradient: 359 shade = 255 - shade 360 shade = '#%02x%02x%02x' % (shade, shade, shade,) 361 362 if symbolizer == PointSymbolizer: 363 rule.PointSymbolizer.Graphic.Mark.Fill.CssParameters[0].Value = shade 364 elif symbolizer == LineSymbolizer: 365 rule.LineSymbolizer.Stroke.CssParameters[0].Value = shade 366 elif symbolizer == PolygonSymbolizer: 367 rule.PolygonSymbolizer.Stroke.CssParameters[0].Value = '#000000' 368 rule.PolygonSymbolizer.Fill.CssParameters[0].Value = shade 369 370 # now add the filters 371 if i > 0: 372 f_low = Filter(rule) 373 f_low.PropertyIsGreaterThan = PropertyCriterion(f_low, 'PropertyIsGreaterThan') 374 f_low.PropertyIsGreaterThan.PropertyName = propertyname 375 f_low.PropertyIsGreaterThan.Literal = str(q.bins[i-1]) 376 377 f_high = Filter(rule) 378 f_high.PropertyIsLessThanOrEqualTo = PropertyCriterion(f_high, 'PropertyIsLessThanOrEqualTo') 379 f_high.PropertyIsLessThanOrEqualTo.PropertyName = propertyname 380 f_high.PropertyIsLessThanOrEqualTo.Literal = str(qbin) 381 382 383 if i > 0: 384 rule.Filter = f_low + f_high 385 else: 386 rule.Filter = f_high 387 388 thesld.normalize() 389 390 return thesld
391