0

I'm building a Django app in which I want users to be able to select a location by clicking on a map. That location is then stored in a Postgres database with the PostGIS extension.

Displaying the map in the form works fine, but the form's PointField that uses the OSMWidget does not return lat/lon values; when I print the return value I get something like SRID=4326;POINT (976253.7252582712 6477844.507319077)

How can I get the widget to return lat/lon values, or, alternatively, how can I convert these values before storing them in the database?

Some code snippets:

# forms.py

from django.contrib.gis import forms

class NewEntryForm(forms.Form):
   # ...
   location_name = forms.CharField(label='Name', max_length=20)
   location = forms.PointField(widget=forms.OSMWidget(
                  attrs={
                         'map_width': 600,
                         'map_height': 400,
                         'default_lat': 50.1091,
                         'default_lon': 8.6819,
                         'default_zoom': 9
                        }))

models.py

from django.contrib.gis.db import models

class Place(models.Model):
   name = models.CharField(max_length=20)
   coord = models.PointField(blank=True, null=True)

   def __str__(self):
      return self.name

views.py

from .models import Place
from .forms import NewEntryForm

def new_entry(request):
   if request.method == 'POST':
      form = NewEntryForm(request.POST)
      entries = request.POST
      if form.is_valid():
         new_place = Place(
                           name = request.POST['location_name'],
                           coord = request.POST['location']
                          )

         new_place.save()

Storing a point via the admin GUI works fine, but if I use the admin GUI to look at the locations stored via above form it's clear that things are not working correctly.

admin.py

from django.contrib.gis import admin
from .models import Place

@admin.register(Place)

class PlaceAdmin(admin.OSMGeoAdmin):
   pass

I'm using Django v. 3.0.5 with Python v. 3.6.10 on Ubuntu 16.04.

2 Answers 2

1

I noticed that the output of OSMWidget I posted in my question was actually printed after I had assigned it to the coordinates in my Place model. The actual widget output reads {"type":"Point","coordinates":[1011414.7582694523,6496495.14222066]}.

From this answer I gathered that the srid of the coordinates returned by the widget is 3857; my database uses 4326. What has solved my problem is writing an auxiliary function to reformat the widget output so that the coordinates will be automatically converted before the new Place object is saved.

# aux.py
from re import search

def format_pointstr_from_osmwidget(osm_output):
   srid = 3857

   pattern = '([0-9.]+,[0-9.]+)'

   c = search(pattern, osm_output).group(0).split(sep=',')

   coord = { 
            'srid': srid,
            'lat': c[0],
            'lon': c[1]
           }
   pointstr = "SRID={srid};POINT({lat} {lon})".format(**coord)

   return pointstr

In views.py I now reformat the widget output before creating a new Place object:

from .aux import format_pointstr_from_osmwidget

#...

def new_entry(request):
   if request.method == 'POST':
      form = NewEntryForm(request.POST)

   if form.is_valid():
         pointstr = format_pointstr_from_osmwidget(request.POST['location'])
         new_place = Place(
                           name = request.POST['location_name'],
                           coord = pointstr
                          )
         new_place.save()
0

You can use the form's save method to save an instance of your Place to the db. This should work fine:

def new_entry(request):

    if request.method == 'POST':
        form = NewEntryForm(request.POST)

    if form.is_valid():
        form.save()

The spatial transformation from SRID 3857 to SRID 4326 happens automatically, which you should be able to verify like this:

if form.is_valid():
    form.cleaned_data['location'].srs
    form.cleaned_data['location'].srid
    form.cleaned_data['location'].srs
    form.cleaned_data['location'].ewkt

These props should all tell you something like the output of the latter: SRID=4326;POINT (-12.30 54.002) The location in your cleaned data should be a Point object ($ form.cleaned_data['location'] -> <Point object at 0x10a463a10>)

The same should happen if you pass a Point into your model when creating the instance:

import json
...
    coord = Point(json.loads(request.POST['location']))

Notice, you passed the JSON string into it in your example above. This saves you from parsing it yourself (Let the JSON lib do that for you!) and doing a transformation (Let the GIS lib do that for you!).

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.