PostGIS 1.5 allows distance queries on non-point geographic geometry columns with `ST_Distance_Sphere`, enabled this functionality.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12890 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2010-03-30 23:15:43 +00:00
parent dafc077e4a
commit 7bdb9a90d0
4 changed files with 53 additions and 35 deletions

View File

@ -536,7 +536,9 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
op = op(self.geom_func_prefix, value[1]) op = op(self.geom_func_prefix, value[1])
elif lookup_type in self.distance_functions and lookup_type != 'dwithin': elif lookup_type in self.distance_functions and lookup_type != 'dwithin':
if not field.geography and field.geodetic(self.connection): if not field.geography and field.geodetic(self.connection):
# Geodetic distances are only availble from Points to PointFields. # Geodetic distances are only availble from Points to
# PointFields on PostGIS 1.4 and below.
if not self.connection.ops.geography:
if field.geom_type != 'POINT': if field.geom_type != 'POINT':
raise ValueError('PostGIS spherical operations are only valid on PointFields.') raise ValueError('PostGIS spherical operations are only valid on PointFields.')

View File

@ -265,13 +265,23 @@ class DistanceTest(unittest.TestCase):
def test05_geodetic_distance_lookups(self): def test05_geodetic_distance_lookups(self):
"Testing distance lookups on geodetic coordinate systems." "Testing distance lookups on geodetic coordinate systems."
if not oracle: # Line is from Canberra to Sydney. Query is for all other cities within
# Oracle doesn't have this limitation -- PostGIS only allows geodetic # a 100km of that line (which should exclude only Hobart & Adelaide).
# distance queries from Points to PointFields on geometry columns (geography line = GEOSGeometry('LINESTRING(144.9630 -37.8143,151.2607 -33.8870)', 4326)
# columns don't have that limitation). dist_qs = AustraliaCity.objects.filter(point__distance_lte=(line, D(km=100)))
mp = GEOSGeometry('MULTIPOINT(0 0, 5 23)')
self.assertRaises(ValueError, len, if oracle or connection.ops.geography:
AustraliaCity.objects.filter(point__distance_lte=(mp, D(km=100)))) # Oracle and PostGIS 1.5 can do distance lookups on arbitrary geometries.
self.assertEqual(9, dist_qs.count())
self.assertEqual(['Batemans Bay', 'Canberra', 'Hillsdale',
'Melbourne', 'Mittagong', 'Shellharbour',
'Sydney', 'Thirroul', 'Wollongong'],
self.get_names(dist_qs))
else:
# PostGIS 1.4 and below only allows geodetic distance queries (utilizing
# ST_Distance_Sphere/ST_Distance_Spheroid) from Points to PointFields
# on geometry columns.
self.assertRaises(ValueError, dist_qs.count)
# Ensured that a ValueError was raised, none of the rest of the test is # Ensured that a ValueError was raised, none of the rest of the test is
# support on this backend, so bail now. # support on this backend, so bail now.

View File

@ -160,21 +160,26 @@ to be in the units of the field.
.. note:: .. note::
For PostGIS users, the routine ``ST_distance_sphere`` For users of PostGIS 1.4 and below, the routine ``ST_Distance_Sphere``
is used by default for calculating distances on geographic coordinate systems is used by default for calculating distances on geographic coordinate systems
-- which may only be called with point geometries [#fndistsphere]_. Thus, (e.g., WGS84) -- which may only be called with point geometries [#fndistsphere14]_.
geographic distance lookups on traditional PostGIS geometry columns are Thus, geographic distance lookups on traditional PostGIS geometry columns are
only allowed on :class:`PointField` model fields using a point for the only allowed on :class:`PointField` model fields using a point for the
geometry parameter. geometry parameter.
.. note:: .. note::
PostGIS 1.5 introduced :ref:`geography columns <geography-type>`, which In PostGIS 1.5, ``ST_Distance_Sphere`` does *not* limit the geometry types
is limited on what geometry types distance queries are performed with. In geographic distance queries are performed with. [#fndistsphere15]_ However,
other words, if you have ``geography=True`` in your geometry field these queries may take a long time, as great-circle distances must be
definition you'll be allowed to peform arbitrary distance queries with your calculated on the fly for *every* row in the query. This is because the
data in geodetic units of WGS84. spatial index on traditional geometry fields cannot be used.
For much better performance on WGS84 distance queries, consider using
:ref:`geography columns <geography-type>` in your database instead because
they are able to use their spatial index in distance queries.
You can tell GeoDjango to use a geography column by setting ``geography=True``
in your field definition.
For example, let's say we have a ``SouthTexasCity`` model (from the For example, let's say we have a ``SouthTexasCity`` model (from the
`GeoDjango distance tests`__ ) on a *projected* coordinate system valid for cities `GeoDjango distance tests`__ ) on a *projected* coordinate system valid for cities
@ -300,5 +305,6 @@ Method PostGIS Oracle SpatiaLite
.. [#fnwkt] *See* Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049 (May 5, 1999), at Ch. 3.2.5, p. 3-11 (SQL Textual Representation of Geometry). .. [#fnwkt] *See* Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049 (May 5, 1999), at Ch. 3.2.5, p. 3-11 (SQL Textual Representation of Geometry).
.. [#fnewkb] *See* `PostGIS EWKB, EWKT and Canonical Forms <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#EWKB_EWKT>`_, PostGIS documentation at Ch. 4.1.2. .. [#fnewkb] *See* `PostGIS EWKB, EWKT and Canonical Forms <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#EWKB_EWKT>`_, PostGIS documentation at Ch. 4.1.2.
.. [#fngeojson] *See* Howard Butler, Martin Daly, Allan Doyle, Tim Schaub, & Christopher Schmidt, `The GeoJSON Format Specification <http://geojson.org/geojson-spec.html>`_, Revision 1.0 (June 16, 2008). .. [#fngeojson] *See* Howard Butler, Martin Daly, Allan Doyle, Tim Schaub, & Christopher Schmidt, `The GeoJSON Format Specification <http://geojson.org/geojson-spec.html>`_, Revision 1.0 (June 16, 2008).
.. [#fndistsphere] *See* PostGIS 1.5 ``ST_distance_sphere`` `documentation <http://postgis.refractions.net/documentation/manual-1.5/ST_Distance_Sphere.html>`_. .. [#fndistsphere14] *See* `PostGIS 1.4 documentation <http://postgis.refractions.net/documentation/manual-1.4/ST_Distance_Sphere.html>`_ on ``ST_distance_sphere``.
.. [#fndistsphere15] *See* `PostGIS 1.5 documentation <http://postgis.refractions.net/documentation/manual-1.5/ST_Distance_Sphere.html>`_ on ``ST_distance_sphere``.
.. [#] MySQL only supports bounding box operations (known as minimum bounding rectangles, or MBR, in MySQL). Thus, spatial lookups such as :lookup:`contains <gis-contains>` are really equivalent to :lookup:`bbcontains`. .. [#] MySQL only supports bounding box operations (known as minimum bounding rectangles, or MBR, in MySQL). Thus, spatial lookups such as :lookup:`contains <gis-contains>` are really equivalent to :lookup:`bbcontains`.

View File

@ -107,10 +107,11 @@ a flat surface is a straight line, the shortest path between two points on a cur
surface (such as the earth) is an *arc* of a `great circle`__. [#fnthematic]_ Thus, surface (such as the earth) is an *arc* of a `great circle`__. [#fnthematic]_ Thus,
additional computation is required to obtain distances in planar units (e.g., additional computation is required to obtain distances in planar units (e.g.,
kilometers and miles). Using a geographic coordinate system may introduce kilometers and miles). Using a geographic coordinate system may introduce
complications for the developer later on. For example, PostGIS does not complications for the developer later on. For example, PostGIS versions 1.4
have the capability to perform distance calculations between non-point and below do not have the capability to perform distance calculations between
geometries using geographic coordinate systems, e.g., constructing a query to non-point geometries using geographic coordinate systems, e.g., constructing a
find all points within 5 miles of a county boundary stored as WGS84. [#fndist]_ query to find all points within 5 miles of a county boundary stored as WGS84.
[#fndist]_
Portions of the earth's surface may projected onto a two-dimensional, or Portions of the earth's surface may projected onto a two-dimensional, or
Cartesian, plane. Projected coordinate systems are especially convenient Cartesian, plane. Projected coordinate systems are especially convenient
@ -123,9 +124,10 @@ calculations.
.. note:: .. note::
If you wish to peform arbitrary distance queries using non-point If you wish to peform arbitrary distance queries using non-point
geometries, consider using PostGIS 1.5 and enabling the geometries in WGS84, consider upgrading to PostGIS 1.5. For
:attr:`GeometryField.geography` keyword to use the better performance, enable the :attr:`GeometryField.geography`
:ref:`geography database type <geography-type>` instead. keyword so that :ref:`geography database type <geography-type>`
is used instead.
Additional Resources: Additional Resources:
@ -182,7 +184,7 @@ three-dimensonal support.
.. attribute:: GeometryField.geography .. attribute:: GeometryField.geography
If set to ``True``, this option will use create a database column of If set to ``True``, this option will create a database column of
type geography, rather than geometry. Please refer to the type geography, rather than geometry. Please refer to the
:ref:`geography type <geography-type>` section below for more :ref:`geography type <geography-type>` section below for more
details. details.
@ -223,8 +225,6 @@ For more information, the PostGIS documentation contains a helpful section on
determining `when to use geography data type over geometry data type determining `when to use geography data type over geometry data type
<http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_GeographyVSGeometry>`_. <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_GeographyVSGeometry>`_.
``GeoManager`` ``GeoManager``
============== ==============
@ -262,5 +262,5 @@ for example::
.. [#fnsrid] Typically, SRID integer corresponds to an EPSG (`European Petroleum Survey Group <http://www.epsg.org>`_) identifier. However, it may also be associated with custom projections defined in spatial database's spatial reference systems table. .. [#fnsrid] Typically, SRID integer corresponds to an EPSG (`European Petroleum Survey Group <http://www.epsg.org>`_) identifier. However, it may also be associated with custom projections defined in spatial database's spatial reference systems table.
.. [#fnharvard] Harvard Graduate School of Design, `An Overview of Geodesy and Geographic Referencing Systems <http://www.gsd.harvard.edu/gis/manual/projections/fundamentals/>`_. This is an excellent resource for an overview of principles relating to geographic and Cartesian coordinate systems. .. [#fnharvard] Harvard Graduate School of Design, `An Overview of Geodesy and Geographic Referencing Systems <http://www.gsd.harvard.edu/gis/manual/projections/fundamentals/>`_. This is an excellent resource for an overview of principles relating to geographic and Cartesian coordinate systems.
.. [#fnthematic] Terry A. Slocum, Robert B. McMaster, Fritz C. Kessler, & Hugh H. Howard, *Thematic Cartography and Geographic Visualization* (Prentice Hall, 2nd edition), at Ch. 7.1.3. .. [#fnthematic] Terry A. Slocum, Robert B. McMaster, Fritz C. Kessler, & Hugh H. Howard, *Thematic Cartography and Geographic Visualization* (Prentice Hall, 2nd edition), at Ch. 7.1.3.
.. [#fndist] This isn't impossible using GeoDjango; you could for example, take a known point in a projected coordinate system, buffer it to the appropriate radius, and then perform an intersection operation with the buffer transformed to the geographic coordinate system. .. [#fndist] This limitation does not apply to PostGIS 1.5. It should be noted that even in previous versions of PostGIS, this isn't impossible using GeoDjango; you could for example, take a known point in a projected coordinate system, buffer it to the appropriate radius, and then perform an intersection operation with the buffer transformed to the geographic coordinate system.
.. [#fngeography] Please refer to the `PostGIS Geography Type <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_Geography>`_ documentation for more details. .. [#fngeography] Please refer to the `PostGIS Geography Type <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_Geography>`_ documentation for more details.