[GIS] Displaying PostgGIS geometry on GeoDjango map widget

geodjangopostgis

I'm trying to display a custom map feature on a GeoDjango map. The geometry is a multipolygon stored in a PostGIS database backend.

And I have trouble to find the correct piece of documentation that explains how to load and add my geometry from the database. This is what my PostGIS contains:

SELECT * FROM aptroomat_worldborder;

postgis table

This is my model.py equivalent in my GeoDjango project:

from django.contrib.gis.db import models

class WorldBorder(models.Model):
    name = models.CharField(max_length=50)
    area = models.IntegerField()
    pop2005 = models.IntegerField('Population 2005')
    fips = models.CharField('FIPS Code', max_length=2)
    iso2 = models.CharField('2 Digit ISO', max_length=2)
    iso3 = models.CharField('3 Digit ISO', max_length=3)
    un = models.IntegerField('United Nations Code')
    region = models.IntegerField('Region Code')
    subregion = models.IntegerField('Sub-Region Code')
    lon = models.FloatField()
    lat = models.FloatField()
    mpoly = models.MultiPolygonField()
    objects = models.GeoManager()

    def __str__(self):
        return self.name

This is how I define my form in forms.py:

from django.contrib.gis import forms

class WorldBorderForm(forms.Form):
    world = forms.MultiPolygonField(widget = 
        forms.OSMWidget(attrs = {'map_width': 1024, 'map_height': 600}))

And my views.py containing the callback:

from django.shortcuts import render
from myproject.forms import WorldBorderForm

def index(request):
    form = WorldBorderForm()
    context = { 'form': form }
    return render(request, 'myproject/index.html', context)

This is how the result looks including my template, using an OSM base widget:

empty osm map

This is pretty much what I get from reading the GeoDjango documentation and following the GeoDjango tutorial on World Borders.

But the missing piece in the docs is: How to display my geometries from the PostGIS database in my GeoDjango map widget? I can't seem to figure out how to connect both.

Any ideas?

Source code is on github.com/donSchoe/sabracta.

Versions used in this project are:

  • python 3.4.3
  • postgresql 9.4.1
  • postgis 2.1.5
  • django 1.7.4

Best Answer

If you want to display your shapefiles on the map canvas, it can be done by creating .kml files.

First in models.py create functions to convert data to .kml:

def shpPoint(request):
  points = ShapefilePoint.objects.kml()
  return render_to_kml("placemarks.kml", {'places': points})

placemarks.kml is a simple template for that purpose, it can be found in geodjango documentation I guess:

{% extends "base.kml" %}
{% block placemarks %}{% for place in places %}
  <Placemark>
    <name>{% if place.name %}{{ place.name }}{% else %}{{ place }}{% endif %}</name>
    <description>{% if place.description %}{{ place.description }}{% else %}{{ place }}{% endif %}</description>
    {{ place.kml|safe }}
  </Placemark>{% endfor %}{% endblock %}

and function to rendering:

def index(request):
  return render_to_response("map.html")

Now map your kml link in urls.py:

...
url(r'^point/', shpPoint),
...

And finally you can add your layer in JS code (this code contains example styling for point layer):

var PointLayer = new ol.layer.Vector({
  title: 'Point',
    source: new ol.source.KML({
        projection:new ol.proj.get("EPSG:3857"),
    url:'http://localhost:8000/point/',
    extractStyles: false
    }),
  style: (function() {
  var textStroke = new ol.style.Stroke({
    color: 'yellow',
    width: 3
  });
  var textFill = new ol.style.Fill({
    color: 'black'
  });
  return function(feature, resolution) {
    return [new ol.style.Style({
      image: new ol.style.Circle({
      radius: 7,
      fill: new ol.style.Fill({color: 'yellow'}),
      stroke: new ol.style.Stroke({color: 'red'})
    }),
      text: new ol.style.Text({
        font: '11px arial,sans-serif',
        text: feature.get('name'),
        fill: textFill,
        stroke: textStroke,
        offsetX: 25,
        offsetY: -10
      })
    })];
  };
})()

});
map.addLayer(PointLayer);