12

I've got geodjango running using openlayers and OpenStreetMaps with the admin app.

Now I want to write some views to display the data. Basically, I just want to add a list of points (seen in the admin) to the map.

Geodjango appears to use a special openlayers.js file to do it's magic in the admin. Is there a good way to interface with this?

How can I write a view/template to display the geodjango data on a open street map window, as is seen in the admin?

At the moment, I'm digging into the openlayers.js file and api looking for an 'easy' solution. (I don't have js experience so this is taking some time.)

The current way I can see to do this is add the following as a template, and use django to add the code needed to display the points. (Based on the example here)

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Draw Feature Example</title>

        <script src="http://www.openlayers.org/api/OpenLayers.js"></script>
      <script type="text/javascript">
            var map;

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

                /*
                 * Layer style
                 */
                // we want opaque external graphics and non-opaque internal graphics
                var layer_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
                layer_style.fillOpacity = 0.2;
                layer_style.graphicOpacity = 1;

                /*
                 * Blue style
                 */
                var style_blue = OpenLayers.Util.extend({}, layer_style);
                style_blue.strokeColor = "blue";
                style_blue.fillColor = "blue";
                style_blue.graphicName = "star";
                style_blue.pointRadius = 10;
                style_blue.strokeWidth = 3;
                style_blue.rotation = 45;
                style_blue.strokeLinecap = "butt";

                var vectorLayer = new OpenLayers.Layer.Vector("Simple Geometry", {style: layer_style});

                // create a point feature
                var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
                var pointFeature = new OpenLayers.Feature.Vector(point,null,style_blue);
                // Add additional points/features here via django

                map.addLayer(vectorLayer);
                map.setCenter(new OpenLayers.LonLat(point.x, point.y), 5);
                vectorLayer.addFeatures([pointFeature]);
            }
        </script>
    </head>
    <body onload="init()">
        <div id="map" class="smallmap"></div>
    </body>
</html>

Is this how it's done, or is there a better way?

monkut
  • 42,176
  • 24
  • 124
  • 155

5 Answers5

5

Another solution is to create a form that utilizes the GeoDjango Admin widget.

To do this, I:

Setup a GeneratePolygonAdminClass:

class GeneratePolygonAdmin(admin.GeoModelAdmin):
    list_filter=('polygon',)
    list_display=('object', 'polygon')

Where the form is built:

geoAdmin=GeneratePolygonAdmin(ModelWithPolygonField, admin.site)
PolygonFormField=GeneratePolygon._meta.get_field('Polygon')
PolygonWidget=geoAdmin.get_map_widget(PolygonFormField)
Dict['Polygon']=forms.CharField(widget=PolygonWidget())  #In this case, I am creating a Dict to use for a dynamic form

Populating the widget of the form:

def SetupPolygonWidget(form, LayerName, MapFileName, DefaultPolygon=''):
    form.setData({'Polygon':DefaultPolygon})
    form.fields['Polygon'].widget.params['wms_layer']=LayerName
    form.fields['Polygon'].widget.params['wms_url']='/cgi-bin/mapserv?MAP=' + MapFileName
    form.fields['Polygon'].widget.params['default_lon']=-80.9
    form.fields['Polygon'].widget.params['default_lat']=33.7
    form.fields['Polygon'].widget.params['default_zoom']=11
    form.fields['Polygon'].widget.params['wms_name']=YOURWMSLayerName
    form.fields['Polygon'].widget.params['map_width']=800
    form.fields['Polygon'].widget.params['map_height']=600
    form.fields['Polygon'].widget.params['map_srid']=YOUR_SRID
    form.fields['Polygon'].widget.params['modifiable']=True
    form.fields['Polygon'].widget.params['map_options']={}
    form.fields['Polygon'].widget.params['map_options']['buffer'] = 0   
    return form

Based on the code at: http://code.djangoproject.com/browser/django/branches/gis/django/contrib/gis/admin/options.py?rev=7980

It looks like you can use the extra_js option to include OpenStreetMap (I have not tested this).

Gregory Roby
  • 73
  • 1
  • 6
3

This is quite old, and I wouldn't go around creating a template hack as I originally was thinking. Now I would use leaflet.js with an ajax request to a django view that returns geojson to a leaflet geojson layer.

This makes the django side super easy.

Sample Django View:

# -*- coding: utf-8 -*-
'''
'''
import json
from django.http import HttpResponse, HttpResponseBadRequest
from django.contrib.gis.geos import Polygon

from models import ResultLayer, MyModel

def get_layer_polygons(request, layer_id):
    """
    Return the polygons for the given bbox (bounding box)
    """
    layer = ResultLayer.objects.get(id=layer_id)    
    bbox_raw = request.GET.get("bbox", None)

    # Make sure the incoming bounding box is correctly formed!
    bbox = None
    if bbox_raw and bbox_raw.count(",") == 3:        
        bbox = [float(v) for v in bbox_raw.split(",")]     
    if not bbox:
        msg = "Improperly formed or not given 'bbox' querystring option, should be in the format '?bbox=minlon,minlat,maxlon,maxlat'"
        return HttpResponseBadRequest(msg)

    bbox_poly = Polygon.from_bbox(bbox)
    bbox_poly.srid = 900913 # google
    bbox_poly.transform(layer.srid) # transform to the layer's srid for querying  

    bin_size = int(bin_size)
    # build vector polygons from bin
    results = MyModel.objects.filter(layer=layer, poly__intersects=bbox_poly).transform(900913, field_name="poly")
    geojson_data = []
    for r in results:
        # loading json in order to dump json list later
        gjson = r.poly.geojson
        py_gjson = json.loads(gjson)
        geojson_data.append(py_gjson)
    return HttpResponse(json.dumps(geojson_data), mimetype='application/json')
monkut
  • 42,176
  • 24
  • 124
  • 155
2

I think your solution is workable and probably the easiest approach. Just templatize the javascript and use Django to inject your data points as the template is rendered.

If you wanted to get fancier, you could have a Django view that served up the data points as JSON (application/json) and then use AJAX to call back and retrieve the data based on events that are happening in the browser. If you want your application to be highly interactive above and beyond what OpenLayers provides, this might be worth the added complexity, but of course it all depends on the needs of your application.

Joe Holloway
  • 28,320
  • 15
  • 82
  • 92
  • I think the best method is to create a django view that returns the necessary JSON object for the necessary data. – monkut Jan 13 '12 at 04:58
2

You could consider using FloppyForms. In the end, I usually end up customizing the solution to my own needs, but it's a nice way to get started.

ivy
  • 5,539
  • 1
  • 34
  • 48
0

Checkout this tutorial from the geodjango-basic-apps project:
http://code.google.com/p/geodjango-basic-apps/wiki/FOSS4GWorkshop

maybe you don't have to hack up your own javascript just yet

Anentropic
  • 32,188
  • 12
  • 99
  • 147