2

I'm building a geospatial mapping application with Geodjango. I have created a model which has a MultiPolygonField():

class newJob(models.Model):
    job_name = models.CharField(max_length=64)
    job_user = models.CharField(max_length=64)
    job_desc = models.CharField(max_length=64)
    job_loc = models.MultiPolygonField()

I have a simple function-based view to gather the required data from the database:

def viewjob(request):

        req = request.GET
        print(req)

        job_name = newJob.objects.values_list('job_name', flat=True).get(pk=req['search'])
        job_desc = newJob.objects.values_list('job_desc', flat=True).get(pk=req['search'])
        job_loc = newJob.objects.values_list('job_loc', flat=True).get(pk=req['search'])

        print(job_loc)
        print(type(job_loc))

        context = {

        'job_name': job_name,
        'job_desc': job_desc,
        'job_loc': job_loc

        }

        return render(request,'ecomap/viewjob.html', context)

The variable job_loc is of class django.contrib.gis.geos.collections.MultiPolygon. An example looks like:

SRID=4326;MULTIPOLYGON (((0.9008789062500006 52.42252295423906, 0.3900146484375003 52.05586831074773, 0.5657958984375002 51.73043054294018, 1.2469482421875 51.90700188674099, 1.6754150390625 52.22443459871997, 1.2139892578125 52.43926935464697, 0.9008789062500006 52.42252295423906)))

My basic template looks like:

{% extends "base.html" %}

{% block body %}

{% load leaflet_tags %}
{% leaflet_js %}
{% leaflet_css %}


<script type="text/javascript">
    function map_init(map, options) {
           ...
           ...
           ...

    }
</script>
  <h1>{{ job_name }}</h1>
  <h3>{{ job_desc }}</h3>


  {{job_loc}}

  <br>

  <h5>Your Area</h5>
  {% leaflet_map "yourmap" callback="window.map_init_basic" %}

 {% endblock %}

I was hoping it would be as easy as passing job_loc to Leaflet given the data is spatial but I'm thinking that there are perhaps more intermediate stages to get the data in the right format for plotting.

What is the easiest way to plot the polygon represented in job_loc on a leaflet map embedded in my template?

3
  • Did you check out the Leaflet requirements at leafletjs.com/reference-1.4.0.html#polygon so that you can match them? Jan 31 at 19:10
  • I'd suggest returning the data as JSON and catching in leaflet with ajax. I personally try and avoid using the template languages
    – ziggy
    Jan 31 at 20:00
  • @ziggy do you have an example of your approach which utilises (geo)django, Ajax and Leaflet? My background is data science not webdev, so I've got a massive learning curve entering the Django space. Having said that, I'm keen to learn different approaches.
    – Simon
    Jan 31 at 20:23
2

In your model, try adding a property to build a list of coordinates, which seems required by Leaflet as you can see here:

Leaflet Polygon
Source: https://leafletjs.com/reference-1.4.0.html#polygon

I would go something this way;

class newJob(models.Model):
    job_name = models.CharField(max_length=64)
    job_user = models.CharField(max_length=64)
    job_desc = models.CharField(max_length=64)
    job_loc = models.MultiPolygonField()

    @property
    def job_coordinates_list(self):
        return [[point.x, point.y] for point in self.job_loc]

Then in your map:

<script type="text/javascript">
    function map_init(map, options) {

        ...
        L.polygon({
            { job_loc.job_coordinates_list }}, {color: 'blue'}
        ).bindPopup({{ job_loc.job_name }}).addTo(map)
        ...

    }
</script>

As I do not have your project at hands, you will probably have to do some extra, but if you got the idea you should be able to deal with it on your own (for example, you will probably have to loop over your single polygons with a for loop {% for single_poly in job_loc %}).

In addition, you will of course need to ensure that the coordinate reference system of your input data matches that of your map. This goes without saying.

2
  • I'm not sure I'm following your instructions properly, else I'm missing something simple (I'm new to Django). The code in the template can't access the property decorator (job_coordinates_list) in models.py. I re-ran migrations but still no luck.
    – Simon
    Jan 31 at 20:26
  • 1
    See if this helps, you may need to call it from your view: stackoverflow.com/a/1333277/6630397 Jan 31 at 20:31
1

Here is an answer that will use a JSON response from your view and grab it using leaflet ajax. I don't know your data and rarely use the django methods to return SQL data but what you could do is just return the table you want as JSON. Returning it as JSON you will have to figure out yourself (could use Django or if its in postgres just in the db connection and use SQL to return the table as JSON)

In your views.py

from django.db import connection as conn
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse

@csrf_exempt
def viewjob(request):    
    req = request.GET
    ##returning table as JSON
    cur = conn.cursor()
    qry='''SELECT row_to_json(fc)
      FROM
       ( SELECT 'FeatureCollection' AS TYPE,
               array_to_json(array_agg(f)) AS features
       FROM
         (SELECT 'Feature' AS TYPE,
                 ST_AsGeoJSON(st_transform(g.geom,4326))::JSON AS geometry,
                 row_to_json(
                               (SELECT p
                                FROM
                                  ( SELECT job_name,job_desc,job_loc) AS p)) AS properties
          FROM table AS g
          ) AS f) AS fc;
  '''
cur.execute(qry)
row=cur.fetchone()
return JsonResponse(row[0], safe=False)

then on the frontend side just simple use the leaflet ajax https://github.com/calvinmetcalf/leaflet-ajax to grab the JSON from that URL

var geojsonLayer = new L.GeoJSON.AJAX("http://localhost:8000/name_of_view_in_urls").addTo(map);;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.