KrazyMax
KrazyMax

Reputation: 959

Why is nothing being saved when trying to update geoDjango Point objects?


SOLVED: see my own answer for an explanation.


I am writing an API to help me maintain my database when needed.

One function of my API is for updating a specific geoDjango Point field of all the concerned objects, thanks to a dedicated method of my model (which is pretty useless rergading to EDIT 1, see below).

The issue I have been facing with is the following: nothing seems to be saved to the database when calling that method on several objects, even if a call to save method seems to work while browsing the objects.

See EDIT 2, I figured out this issue was specific to geoDjango Point objects (everything else is being updated as expected).

Here below is my model:

models.py

class Location(geoModels.Model):
    coord_field = geoModels.PointField(blank=True, null=True)
    valTest = models.CharField(max_length=100, default="tagada") # only used to understand why coord_field can't be updated properly
    def update_coord_field(self):
        # some stuff is being done
        self.coord_field = Point(
            newLong,
            newLat,
            srid=3857 # I am trying to update that value
        )
        # nothing more

newLong and newLat are being generated thanks to Google Maps API. As Google Maps seems to be working with SRID 3857 while GeoDjango uses SRID 4326 by default when creating a new Point, I want to force SRID of self.coord_field to SRID 3857 (I have been facing incoherent distance calculus, so I am trying to play with SRID to solve that).

Now here is my script :

script.py

from MyApp import models as MyAppModels
def update_all_coord_field():
    for location in MyAppModels.Location.objects.all():
        print("  old SRID : ", location.coord_field.srid)
        location.update_coord_field()
        location.save()
        print("  new SRID : ", location.coord_field.srid)
update_all_coord_field()

But when trying to execute my script again (or simply just trying to use my new valued attribute), I can only observe that nothing was saved to the db (it still print the old value of location.coord_field.srid for all Locationobjects).

How my script is being executed:

python manage.py shell
exec(script.py)

If I call twice my script, here is the following traceback (only one object to make thinks understandable):

exec(script.py)
  old SRID : 4326
  new SRID : 3857 # so apparently object has been updated...
exec(script.py)
  old SRID: 4326 # ...but this line proves that the updated value has not been pushed to the database
  new SRID: 3857 

Do you have any idea why is this not working?


FIRST EDIT: @JohnMoutafis pointed out to me there was already a function to change SRID of Point objects: transform.

Location.objects.get(pk=pk).transform(3857) # define a new value of SRID

It is usefull, yet it does not solve my issue! Please notice I did not update my code here with that transform method.


SECOND EDIT: I've been through a lot of pain to understand that it is not the save method which is not working! Actually, if I try to update "casual" data field of MyAppModel, everything works fine and is updated as expected. The real issue is only about my coord_field attribute which can't be updated, and I can't figure out why.

In my script I tried on the one hand what I explained above, and on the other hand I also tried the following to update manually (and not through a custom method of my model) coord_field:

MyAppModels.Location.objects.filter(pk=pk).update(
    geoCoord=Point(location.geoCoord.x, location.geoCoord.y, srid=3857),
    valTest="meat"
)

As a result, if I execute again my script, I see that valTest has been properly updated (nice!), but geoCoord still is the old value! :-( With the wrong SRID and everything. So, I think I really have a trouble updating geoDjango Point object in particular, and will update the title of my question as a consequence of that!

Upvotes: 1

Views: 497

Answers (2)

KrazyMax
KrazyMax

Reputation: 959

SOLVED: my conlusion is that once you specified SRID of one Point, you can not change it later. Even the transform method won't change SRID, but will only make some conversion in place, without altering original SRID of your Point. Actually, I think that when I write:

coord_field = geoModels.PointField(blank=True, null=True)

the default 4826 SRID is already set once for all to the appropriate column of the database (and only this SRID can then be used for this column). So, even when I write:

self.coord_field = Point(newLong, newLat, srid=newSRID)

I won't be able to associate another SRID to my Point. Some calculus is going to be computed, but at the end the Point attribute will still be affected of the initial SRID.

To be clear: I am not sure 100% about those conclusions (I did not find any appropriate doc), but deleting coord_field column then setting coord_field as follows:

coord_field = geoModels.PointField(srid=3857, blank=True, null=True)

..did the trick. If anyone has got some clean answer for me, I would still appreciate to read it!

Upvotes: 1

John Moutafis
John Moutafis

Reputation: 23134

Based on this gis.stackexchange post, I will assume that you are declaring each point initially without an explicit srid, therefore GeoDajngo assumes that you must have declared it on 4326.

  1. You need to declare every point in your database explicitly.
    Here is a script to help you do that with your existing points.

    for item in Location.objects.all():
        item.coord_field = Point(item.coord_field.x, item.coord_field.y, srid=4326)
        item.save()
    
  2. You can use transform instead of your custom method to change the srid of your Points:

    Location.objects.all().transform(3857)
    

EDIT due to comment:

Since clarification provided by @KrazyMax on the comments, he needs to calculate the spherical distance. I have made a post about that here: How to calculate 3D distance (including altitude) between two points in GeoDjango

Upvotes: 1

Related Questions