2

In my django application I have a map displayed when I'm in the admin thanks to the class OSMGeoAdmin (from django.contrib.gis.admin) the only problem is that this map kind of reverse the GPS coordinates (lat, long).

Example : Point(48, 2) should point near Orléans (in France) instead of that the map points to (2, 48) which is near Somalia.

It is not problematic for the server, but it is when I want to check those coordinates.

Could anyone help me with this issue please ?

Depado
  • 4,811
  • 3
  • 41
  • 63
  • Why can't you just reverse your check? – scai Sep 02 '13 at 09:14
  • I don't understand your question. The map interpret coordinates in the bad order (long, lat) instead of (lat, long) and I don't know why or how to modify that. – Depado Sep 02 '13 at 09:16
  • 1
    Why is that a bad order? Different softwares have different orders. See [Preferred order of writing latitude & longitude tuples](http://stackoverflow.com/questions/7309121/preferred-order-of-writing-latitude-longitude-tuples). You either have to change your django application accordingly or the source of your lat,lon values. – scai Sep 02 '13 at 09:36
  • I see. I thought it was a standard to use lattitude then longitude. Is there a way I can tell OpenStreetMap to reverse the coordinates ? – Depado Sep 02 '13 at 10:12
  • Where exactly do you get your coordinates from? OpenStreetMap is mainly a database, it doesn't has an order for lat and lon. – scai Sep 02 '13 at 10:52
  • My coordinates are stored in my database. OpenStreetMap is just a layer in the Django admin that displays a map according to the gps coordinates it has found in the DB. But I know that my coordinates are right, I checked them multiple times before. – Depado Sep 02 '13 at 10:56
  • Then this is a configuration problem of the Django OpenStreetMap plugin(?). You have to ask its authors or fix it yourself or reverse the coordinates in your DB. – scai Sep 02 '13 at 10:59

2 Answers2

1

If your coordinates in your db are the wrong way around you should fix that. In ./manage.py shell:

from app.models import Point

for obj in Point.objects.all():
    obj.lat, obj.lng = obj.lng, obj.lat
    obj.save()

Now you only have to fix your 'kind of reverse' problem. The reason why the coordinates are stored to the wrong way around in the first place.

EDIT

If you only want to return a coordinate the other way around you can define a custom method on a model to add custom “row-level” functionality to your objects. See: Model methods.

def _get_reversed_point(self):
    "Returns the reversed point (lng, lat)."
    return (self.point.lng, self.point.lat)
reversed_point = property(get_reversed_point)

Now you can query your db normally and when dealing with reversed points use obj.reversed_point which will return the reversed tuple.

EDIT 2

The reversed_point isn't part of the form and therefore can't be used in the change view unless you add it yourself. This is done by ModelAdmin.form. In the form you can (re)define a field and specify a widget. Create a widget by overridinging an excising widget. It goes like this:

from somewhere import SomeWidget

class MyWidget(SomeWidget):
    def render(self, name, value, attrs=None):
        output = []
        template = '<p>Your html that will display a beautiful map. Point: %(lat)s %(lng)s</p>'
        output.append(template % {'lat':self.instance.point.lat, 'lng':self.instance.point.lng, })
        output.append(super(SomeWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))


class MyForm(forms.ModelForm):
    point = forms.SomeField(widget=MyWidget)

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)

        if hasattr(self, 'instance'):
            # This gives the widget access to all fields in MyModel object.
            self.fields['point'].widget.instance = self.instance

    class Meta:
        model = MyModel
        exclude = []

class MyAdmin(admin.ModelAdmin):
    form = MyForm

GeoDjango comes with some widgets. Reading their code is a good starting point and will learn you where the widgets apply the point info the 'wrong' way around. Good luck.

allcaps
  • 10,945
  • 1
  • 33
  • 54
  • The coordinates are in the right way in the db. The problem seems to be that openstreetmap uses longitude first and then latitude. – Depado Sep 02 '13 at 10:11
  • No, it must be some tool or service you are using which has a different order. OSM doesn't define a fixed order. – scai Sep 02 '13 at 10:54
  • Well maybe it doesn't interpret it right ? Since it doesn't define an order... All my objects have the right properties which means they are stored in my db (lat, lng). The fact is that I don't have control over OSM and I don't know how to modify the way it works. – Depado Sep 02 '13 at 13:21
  • Please don't refer to it as "OSM", this is just an inofficial extension to Django and doesn't really have much to do with the OSM project. Either way you have to reverse your coordinates at some point and @allcaps seems to have the correct solution for you. – scai Sep 02 '13 at 14:14
  • I will try to do that. Thanks for your patience =) – Depado Sep 03 '13 at 07:35
  • By the way could you give me a hint on how to use that function in the django admin ? I mean all the fields I display in the admin are defined by strings {'fields':['gps']} and that's what displays the map. – Depado Sep 03 '13 at 12:25
  • Yes I did even if I'm struggling with Django at the moment haha :p – Depado Sep 04 '13 at 10:45
  • point = forms.SomeField(widget=MyWidget) What should I put instead of SomeField ? I don't get it... – Depado Sep 04 '13 at 13:13
  • 1
    A form field. Build in form fields: https://docs.djangoproject.com/en/1.5/ref/forms/fields/ Geo Django fields live in django.contrib.gis.forms.fields eg: from django.contrib.gis.forms.fields import GeometryField – allcaps Sep 05 '13 at 09:53
1

Solved this problem with a simple tweak. Redefining the render method is far more simple and doesn't imply database/model modification. For example :

def get_map_widget(self, db_field):
    mapped = super(MissionAdmin, self).get_map_widget(db_field)

    def render(inner_self, name, value, attrs=None):
        if value and isinstance(value, Point):
            value.x, value.y = value.y, value.x
        return super(mapped, inner_self).render(name, value, attrs)

    mapped.render = render
    return mapped

That implies when the map is being rendered, just before generating the HTML (rendering process) I just reverse the points when displaying them. Of course it's kind of dangerous if, for example, it's not a readonly map. In this case you have to modify the save_model method to reverse again your coordinates.

Depado
  • 4,811
  • 3
  • 41
  • 63
  • And this goes in widget or form? – allcaps Feb 07 '14 at 17:04
  • This goes in the OSMGeoAdmin class ;) – Depado Feb 07 '14 at 17:09
  • That's kind of a quick and dirty fix though. I will reverse every coordinates in the next days because that kind of fix will always break one day. And indeed I got some troubles with editables fields and that method haha – Depado Feb 10 '14 at 09:18
  • Why don't you just conform to the Django order of lat, lng? When using a framework and trivial stuff gets hard, you may start to question your solution. I think the first part of my answer has the cleanest solution. 1) Reverse `lat lng` in db. 2) Create a `reversed_point` model method 3) In your project templates search and replace `.point` in `.reversed_point`. Done. – allcaps Feb 10 '14 at 10:44