from django.contrib.gis.db.models.fields import GeometryField #from django.contrib.gis.gdal import Envelope from django.contrib.gis.geos import Polygon import json from django.http import HttpResponse #from django.db.models.fields.related import ManyRelatedManager # also need to check out: # http://code.google.com/p/dojango/source/browse/trunk/dojango/util/__init__.py#82 # example usages: """ def a_shapes(request): ids = request.GET.get('ids').split(',') mimetype = 'text/plain' #'application/javascript; charset=utf8' pretty_print = True if ids: qs = WorldBorders.objects.filter(affiliates__in=ids).annotate(num_a=Count('affiliates')).filter(num_a__gt=0) else: qs = WorldBorders.objects.none() return render_to_geojson(qs, extra_attributes=['num_a','affiliates_set'], geom_attribute='point', included_fields=['id','name'], mimetype=mimetype, proj_transform=900913, pretty_print=pretty_print ) def responses(qs,type_='countries',pretty_print=True,mimetype='text/plain'): if type_ == 'locations': qs = qs.geolocations() return render_to_geojson(qs, excluded_fields=['json'], geom_field='point', proj_transform=900913, mimetype=mimetype, pretty_print=pretty_print ) elif type_ == 'affiliates': qs = qs.exclude(geokeywords='').attach_locations() return render_to_geojson(qs, included_fields=['id','_geokeywords_cache'], geom_attribute='point', extra_attributes=['name'], proj_transform=900913, mimetype=mimetype, pretty_print=pretty_print ) elif type_ == 'countries': qs2 = W.objects.filter(affiliates__in=qs).annotate(num_a=Count('affiliates')).filter(num_a__gt=0) return render_to_geojson(qs2, extra_attributes=['num_a'], #geom_attribute='point', mimetype=mimetype, pretty_print=pretty_print ) else:# type_ == 'countries' or type is None: if len(qs) > 10: # this is a limit, weird huh? # requires another all() otherwise it # returns a list! qs = qs.all()[:10] return render_to_geojson(qs, included_fields=['id','_geokeywords_cache'], geom_attribute='countries.unionagg', extra_attributes=['name'], mimetype=mimetype, pretty_print=pretty_print ) """ def render_to_geojson(query_set, geom_field=None, geom_attribute=None, extra_attributes=[],mimetype='text/plain', pretty_print=False, excluded_fields=[],included_fields=[],proj_transform=None): ''' Shortcut to render a GeoJson FeatureCollection from a Django QuerySet. Currently computes a bbox and adds a crs member as a sr.org link ''' excluded_fields.append('_state') collection = {} if hasattr(query_set,'_meta'): # its a model instance fields = query_set._meta.fields query_set = [query_set] else: fields = query_set.model._meta.fields if geom_attribute: geometry_name = geom_attribute geo_field = None if '.' in geom_attribute: prop, meth = geom_attribute.split('.') if len(query_set): p = getattr(query_set[0],prop) geo_field = getattr(p,meth) if callable(geo_field): geo_field = geo_field() else: if len(query_set): geo_field = getattr(query_set[0],geom_attribute) if callable(geo_field): geo_field = geo_field() if not geo_field: srid = 4326 else: srid = geo_field.srid else: geo_fields = [f for f in fields if isinstance(f, GeometryField)] #attempt to assign geom_field that was passed in if geom_field: #import pdb;pdb.set_trace() geo_fieldnames = [x.name for x in geo_fields] try: geo_field = geo_fields[geo_fieldnames.index(geom_field)] except: raise Exception('%s is not a valid geometry on this model' % geom_field) else: if not len(geo_fields): raise Exception('There appears to be no valid geometry on this model') geo_field = geo_fields[0] # no support yet for multiple geometry fields #remove other geom fields from showing up in attributes if len(geo_fields) > 1: for field in geo_fields: if field.name not in excluded_fields: excluded_fields.append(field.name) geometry_name = geo_field.name srid = geo_field.srid if proj_transform: to_srid = proj_transform else: to_srid = srid # Gather the projection information crs = {} crs['type'] = "link" crs_properties = {} crs_properties['href'] = 'http://spatialreference.org/ref/epsg/%s/' % to_srid crs_properties['type'] = 'proj4' crs['properties'] = crs_properties collection['crs'] = crs collection['srid'] = to_srid # Build list of features features = [] if query_set.distinct(): for item in query_set: feat = {} feat['type'] = 'Feature' if included_fields: d = {} for f in included_fields: if hasattr(item,f): d[f] = getattr(item,f) else: d = item.__dict__.copy() for field in excluded_fields: if field in d.keys(): d.pop(field) if geometry_name in d: d.pop(geometry_name) for attr in extra_attributes: a = getattr(item,attr) # crappy way of trying to figure out it this is a # m2m, aka 'ManyRelatedManager' if hasattr(a,'values_list'): a = list(a.values_list('id',flat=True)) if callable(a): d[attr] = a() else: d[attr] = a if '.' in geometry_name: prop, meth = geometry_name.split('.') a = getattr(item,prop) g = getattr(a,meth) if callable(g): g = g() else: g = getattr(item,geometry_name) if g: if proj_transform: g.transform(proj_transform) feat['geometry'] = json.loads(g.geojson) feat['properties'] = d features.append(feat) else: pass #features.append({'type':'Feature','geometry': {},'properties':{}}) # Label as FeatureCollection and add Features collection['type'] = "FeatureCollection" collection['features'] = features # Attach extent of all features if query_set: ex = None query_set.query.distinct = False if hasattr(query_set,'agg_extent'): ex = [x for x in query_set.agg_extent.tuple] elif '.' in geometry_name: prop, meth = geometry_name.split('.') a = getattr(item,prop) if a: ex = [x for x in a.extent()] else: # make sure qs does not have .distinct() in it... ex = [x for x in query_set.extent()] if ex: if proj_transform: poly = Polygon.from_bbox(ex) poly.srid = srid poly.transform(proj_transform) ex = poly.extent collection['bbox'] = ex # Return response response = HttpResponse() if pretty_print: response.write('%s' % json.dumps(collection, indent=1)) else: response.write('%s' % json.dumps(collection)) response['Content-length'] = str(len(response.content)) response['Content-Type'] = mimetype return response