What are the default units returned on a distance() or length() operation, and how do I futz with them?

1,624 views
Skip to first unread message

Ben Welsh

unread,
Mar 10, 2009, 2:45:09 PM3/10/09
to geodjango
For instance ... what units are returned here? Decimal degrees? And how could I convert it to miles or another unit of measurement?

>> from django.contrib.gis.geos import GEOSGeometry
>> p1 = GEOSGeometry('POINT (-118.3028050000000064 33.8419159999999977)')
>> p2 = GEOSGeometry('POINT (-118.303404 33.968878)')
>>> p1.distance(p2)
0.12696341301729297

Same goes for something like this ...

>> from django.contrib.gis.geos import LineString
>> line = LineString(p1, p2)
>>> line.length
0.12696341301729297

My confusion comes from the fact I don't see the unit attributes I expected to find in a Distance object, as described in the docs.

>> d1 = p1.distance(p2)
>> dir(d1)
['__abs__', '__add__', '__class__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__ge__', '__getattribute__', '__getformat__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__long__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__setformat__', '__str__', '__sub__', '__truediv__']

To put it simply, my mission is simply to find the distance between two different points, but not using a QuerySet, as described in the documentation.

Per usual, I'm sure there's something obvious I'm missing. Please clue a kid in. Thanks in advance.

Justin Bronn

unread,
Mar 10, 2009, 3:35:13 PM3/10/09
to geod...@googlegroups.com
You're using the `distance` method and `length` property of the
GEOSGeometry objects themselves, rather than through the GeoQuerySet.

In other words, the GeoQuerySet methods operate on the database, and are
able to "know" what the right units are and return an appropriate
Distance object. Whereas, GEOSGeometry objects are "dumb" as to units
because they (at the most) store only an integer id with no information
providing context for the id. Thus, all distances and lengths returned
from GEOSGeometry are "in units of the field" -- e.g., if in WGS84
(SRID=4326), then they are in degrees of latitude and longitude (which
is _not_ a linear unit like meters or feet). Examples:

# This operates at the DB level, and attach new
# `length` and `distance` attributes to the _model_.
GeoModel.objects.length()
GeoModel.objects.distance(other)

# Whereas, this is a model already returned from the database
# and are called on the model's GEOSGeometry representation of
# the geometry.
gm = GeoModel.objects.get(id=1)
# Both of these are just numbers in the units of the field.
gm.line.length
gm.line.distance(other)

The GEOS distance docs are here, and the length docs immediately follow:

http://geodjango.org/docs/geos.html#distance-geom

I'll try to clarify this section better the next time I'm editing the docs.

Regards,
-Justin

Ben Welsh

unread,
Mar 10, 2009, 3:56:00 PM3/10/09
to geod...@googlegroups.com
Thanks for the note Justin. This might be obvious to someone more schooled than me, but what would be the best way to convert the "dumb" distance to another unit of measurement?

Adam Fast

unread,
Mar 10, 2009, 4:19:41 PM3/10/09
to geod...@googlegroups.com
Ben,

The inherent problem is that "degrees" are never a uniform distance
with longitude. (latitude they're 69.1 miles all the time).

I found this code (that I'm sure probably isn't entirely accurate) but
is close enough and compares favorably with tables of data I could
find. It was somewhere in JavaScript, and I basically just "ported" it
over to calculate the degrees of longitude for a distance given a
latitude. Note that if the line's over a larger (?) distance it starts
getting inaccurate, because as the line continues it goes from one
degree being, say 40, to 20mi. In our case we never exceed about 50mi
so it's good enough. Searching Google for the constants mentioned in
the code brings up some powerpoint slides and source code that may be
helpful in getting further than I can get you. You'll be doing the
reverse, but hopefully this will be of help? (Though further
complicating the situation is that unless your lines are only across
latitude or longitude you will need to calculate the latitudinal
difference, longitudinal difference, and then probably use the
pythagorean theorem to get the "real" distance.

I'd recommend looking at GeoPY (http://code.google.com/p/geopy/) if
you just want to avoid hitting the DB. I'm using it for our more
straightforward distance calculation (the VincentyDistance method is
what you'll want) and it's great for that application. Less math /
uncertainty.

Adam


# calculate length of each degree of longitude - they are not
consistent everywhere on earth
# this method is a bit of a "black box" - I'm not positive what
these numbers come from, but
# given reference material and what it outputs it's accurate
latitude_radians = radians(point.y)
p1 = 111412.84
p2 = -93.5
p3 = 0.118

longlen_meters = (p1 * cos(latitude_radians)) + (p2 * cos(3 *
latitude_radians)) + (p3 * cos(5 * latitude_radians))
longlen_mi = longlen_meters * 0.00062137119633838382 # convert
meters into miles
longitude_deg = radius / longlen_mi



2009/3/10 Ben Welsh <ben....@gmail.com>:
Reply all
Reply to author
Forward
0 new messages