views:

333

answers:

1

Hello.

I have just started my first project on GeoDjango.

As a matter of fact, with GeoDjango powered Admin application we all have a great possibility to view/edit spatial data, associated with the current object.

The problem is that after the objects having been populated I need to render several objects' associated geometry at once on a single map. I might implement it as a model action, redirecting to a custom view. I just don't know, how to include the OpenLayers widget in the view and how to render there my compound geometry from my GeoQuerySet.

I would be very thankful for any hint from an experienced GeoDjango programmer.

+2  A: 

Two halves of this question:

  • How do I generate Geographic data that OpenLayers can read via Django?
  • How do I consume this data with OpenLayers?

Generating Geographic Data

There are several different ways to generate geographic data in Django. Built in, you can use the .kml() or .json() methods on a queryset; doing so causes each returned instance to have a .json or .kml property which has the KML or JSON of the Geometry generated as a string.

You can then use this output in templates that use the {{feature.kml}} or {{feature.json}}. (The latter is somewhat difficult, because you would have to manually do the JSON encoding before it hit the template, a bit of an odd situation.)

Another option is to use a library to help you: specifically, vectorformats. (Google "featureserver vectorformats" for information, since I can only include one hyperlink.) Installed via PyPI/easy_install vectorformats, you can use the Django format:

>>> from vectorformats.Formats import Django, GeoJSON
>>> qs = Model.objects.filter(city="Cambridge")
>>> djf = Django.Django(geodjango="geometry", properties=['city', 'state'])
>>> geoj = GeoJSON.GeoJSON()
>>> string = geoj.encode(djf.decode(qs))
>>> print string 

This string can be returned via an HTTPResponse to return a GeoJSON object. So, your view would wrap these 4 lines in a bit that generated a queryset (qs, here), and then returned an HttpResponse with the string.

Consuming Data

OpenLayers has 'format' objects which can read data: There are formats for GeoJSON and KML, as well as others.

You can load the data using standard XMLHttpRequest mechanisms then parse them with a format:

var f = new OpenLayers.Format.GeoJSON();
var features = f.read(req.responseText);
layer.addFeatures(features);

Alternatively, you can use the built in Protocol support to load remote data:

     map = new OpenLayers.Map('map');
     var wms = new OpenLayers.Layer.WMS(
         "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0",
         {layers: 'basic'}
     );

     var layer = new OpenLayers.Layer.Vector("GML", {
         strategies: [new OpenLayers.Strategy.Fixed()],
         protocol: new OpenLayers.Protocol.HTTP({
            url: "/django/view/json/",
            format: new OpenLayers.Format.GeoJSON()
         })
     });

     map.addLayers([wms, layer]);
     map.zoomToExtent(new OpenLayers.Bounds(
         -3.92, 44.34, 4.87, 49.55
     ));

You can see in this example, that the 'url' points to your Django view; all the loading of data and parsing it according to the provided format is included. (You can see a similar example in the OpenLayers example for fixed behavior/http protocol.)

Putting it Together

  1. Create a Django view, using vectorformats to return your data as GeoJSON
  2. Create a separate view, which returns an HTML page like the OpenLayers example linked, with the modifications shown in the code sample.
  3. That view serves the HTML page that loads your GeoJSON data and parses it.
Christopher Schmidt
Thank you, Cristopher, for your answer. Looks very nice and clean, I think it should work. Trying to put it together.
dmytro
Actually, I have browsed GeoDjango Admin app templates and discovered, that authors of GeoDjango have made a special empty block for us in django/contrib/gis/templates/gis/admin/openlayers.js named 'extra_layers'. We could use it through template inheritance to 'inject' js code, adding our extra layers on a map.This eliminates the need to create a separate view for an HTML page with an embedded map like in the above answer in case if the bundled Admin app is OK for us.This is right what I was looking for.Hope this helps somebody.
dmytro