Fixed #25588 -- Added spatial lookups to RasterField.

Thanks Tim Graham for the review.
This commit is contained in:
Daniel Wiesmann 2016-04-21 17:03:14 +01:00 committed by Tim Graham
parent 03efa304bc
commit bbfad84dd9
11 changed files with 743 additions and 158 deletions

View file

@ -53,7 +53,12 @@ Raster Support
--------------
``RasterField`` is currently only implemented for the PostGIS backend. Spatial
queries (such as lookups and distance) are not yet available for raster fields.
lookups are available for raster fields, but spatial database functions and
aggregates aren't implemented for raster fields.
.. versionchanged:: 1.10
``RasterField`` now supports spatial lookups.
Creating and Saving Models with Geometry Fields
===============================================
@ -136,11 +141,20 @@ Spatial Lookups
GeoDjango's lookup types may be used with any manager method like
``filter()``, ``exclude()``, etc. However, the lookup types unique to
GeoDjango are only available on geometry fields.
GeoDjango are only available on spatial fields.
Filters on 'normal' fields (e.g. :class:`~django.db.models.CharField`)
may be chained with those on geographic fields. Thus, geographic queries
take the following general form (assuming the ``Zipcode`` model used in the
:doc:`model-api`)::
may be chained with those on geographic fields. Geographic lookups accept
geometry and raster input on both sides and input types can be mixed freely.
The general structure of geographic lookups is described below. A complete
reference can be found in the :ref:`spatial lookup reference<spatial-lookups>`.
Geometry Lookups
----------------
Geographic queries with geometries take the following general form (assuming
the ``Zipcode`` model used in the :doc:`model-api`)::
>>> qs = Zipcode.objects.filter(<field>__<lookup_type>=<parameter>)
>>> qs = Zipcode.objects.exclude(...)
@ -148,14 +162,60 @@ take the following general form (assuming the ``Zipcode`` model used in the
For example::
>>> qs = Zipcode.objects.filter(poly__contains=pnt)
>>> qs = Elevation.objects.filter(poly__contains=rst)
In this case, ``poly`` is the geographic field, :lookup:`contains <gis-contains>`
is the spatial lookup type, and ``pnt`` is the parameter (which may be a
is the spatial lookup type, ``pnt`` is the parameter (which may be a
:class:`~django.contrib.gis.geos.GEOSGeometry` object or a string of
GeoJSON , WKT, or HEXEWKB).
GeoJSON , WKT, or HEXEWKB), and ``rst`` is a
:class:`~django.contrib.gis.gdal.GDALRaster` object.
A complete reference can be found in the :ref:`spatial lookup reference
<spatial-lookups>`.
.. _spatial-lookup-raster:
Raster Lookups
--------------
.. versionadded:: 1.10
The raster lookup syntax is similar to the syntax for geometries. The only
difference is that a band index can specified as additional input. If no band
index is specified, the first band is used by default (index ``0``). In that
case the syntax is identical to the syntax for geometry lookups.
To specify the band index, an additional parameter can be specified on both
sides of the lookup. On the left hand side, the double underscore syntax is
used to pass a band index. On the right hand side, a tuple of the raster and
band index can be specified.
This results in the following general form for lookups involving rasters
(assuming the ``Elevation`` model used in the :doc:`model-api`)::
>>> qs = Elevation.objects.filter(<field>__<lookup_type>=<parameter>)
>>> qs = Elevation.objects.filter(<field>__<band_index>__<lookup_type>=<parameter>)
>>> qs = Elevation.objects.filter(<field>__<lookup_type>=(<raster_input, <band_index>)
Fore example::
>>> qs = Elevation.objects.filter(rast__contains=geom)
>>> qs = Elevation.objects.filter(rast__contains=rst)
>>> qs = Elevation.objects.filter(rast__1__contains=geom)
>>> qs = Elevation.objects.filter(rast__contains=(rst, 1))
>>> qs = Elevation.objects.filter(rast__1__contains=(rst, 1))
On the left hand side of the example, ``rast`` is the geographic raster field
and :lookup:`contains <gis-contains>` is the spatial lookup type. On the right
hand side, ``geom`` is a geometry input and ``rst`` is a
:class:`~django.contrib.gis.gdal.GDALRaster` object. The band index defaults to
``0`` in the first two queries and is set to ``1`` on the others.
While all spatial lookups can be used with raster objects on both sides, not all
underlying operators natively accept raster input. For cases where the operator
expects geometry input, the raster is automatically converted to a geometry.
It's important to keep this in mind when interpreting the lookup results.
The type of raster support is listed for all lookups in the :ref:`compatibility
table <spatial-lookup-compatibility>`. Lookups involving rasters are currently
only available for the PostGIS backend.
.. _distance-queries:
@ -176,7 +236,7 @@ in the :doc:`model-api` documentation for more details.
Distance Lookups
----------------
*Availability*: PostGIS, Oracle, SpatiaLite
*Availability*: PostGIS, Oracle, SpatiaLite, PGRaster (Native)
The following distance lookups are available:
@ -193,7 +253,7 @@ The following distance lookups are available:
Distance lookups take a tuple parameter comprising:
#. A geometry to base calculations from; and
#. A geometry or raster to base calculations from; and
#. A number or :class:`~django.contrib.gis.measure.Distance` object containing the distance.
If a :class:`~django.contrib.gis.measure.Distance` object is used,
@ -241,6 +301,16 @@ Then distance queries may be performed as follows::
>>> qs = SouthTexasCity.objects.filter(point__distance_gte=(pnt, D(mi=20)))
>>> qs = SouthTexasCity.objects.filter(point__distance_gte=(pnt, D(chain=100)))
Raster queries work the same way by simply replacing the geometry field
``point`` with a raster field, or the ``pnt`` object with a raster object, or
both. To specify the band index of a raster input on the right hand side, a
3-tuple can be passed to the lookup as follows::
>>> qs = SouthTexasCity.objects.filter(point__distance_gte=(rst, 2, D(km=7)))
Where the band with index 2 (the third band) of the raster ``rst`` would be
used for the lookup.
__ https://github.com/django/django/blob/master/tests/gis_tests/distapp/models.py
.. _compatibility-table:
@ -254,43 +324,46 @@ Spatial Lookups
---------------
The following table provides a summary of what spatial lookups are available
for each spatial database backend.
for each spatial database backend. The PostGIS Raster (PGRaster) lookups are
divided into the three categories described in the :ref:`raster lookup details
<spatial-lookup-raster>`: native support ``N``, bilateral native support ``B``,
and geometry conversion support ``C``.
================================= ========= ======== ============ ==========
Lookup Type PostGIS Oracle MySQL [#]_ SpatiaLite
================================= ========= ======== ============ ==========
:lookup:`bbcontains` X X X
:lookup:`bboverlaps` X X X
:lookup:`contained` X X X
:lookup:`contains <gis-contains>` X X X X
:lookup:`contains_properly` X
:lookup:`coveredby` X X
:lookup:`covers` X X
:lookup:`crosses` X X
:lookup:`disjoint` X X X X
:lookup:`distance_gt` X X X
:lookup:`distance_gte` X X X
:lookup:`distance_lt` X X X
:lookup:`distance_lte` X X X
:lookup:`dwithin` X X
:lookup:`equals` X X X X
:lookup:`exact` X X X X
:lookup:`intersects` X X X X
================================= ========= ======== ============ ========== ========
Lookup Type PostGIS Oracle MySQL [#]_ SpatiaLite PGRaster
================================= ========= ======== ============ ========== ========
:lookup:`bbcontains` X X X N
:lookup:`bboverlaps` X X X N
:lookup:`contained` X X X N
:lookup:`contains <gis-contains>` X X X X B
:lookup:`contains_properly` X B
:lookup:`coveredby` X X B
:lookup:`covers` X X B
:lookup:`crosses` X X C
:lookup:`disjoint` X X X X B
:lookup:`distance_gt` X X X N
:lookup:`distance_gte` X X X N
:lookup:`distance_lt` X X X N
:lookup:`distance_lte` X X X N
:lookup:`dwithin` X X B
:lookup:`equals` X X X X C
:lookup:`exact` X X X X B
:lookup:`intersects` X X X X B
:lookup:`isvalid` X
:lookup:`overlaps` X X X X
:lookup:`relate` X X X
:lookup:`same_as` X X X X
:lookup:`touches` X X X X
:lookup:`within` X X X X
:lookup:`left` X
:lookup:`right` X
:lookup:`overlaps_left` X
:lookup:`overlaps_right` X
:lookup:`overlaps_above` X
:lookup:`overlaps_below` X
:lookup:`strictly_above` X
:lookup:`strictly_below` X
================================= ========= ======== ============ ==========
:lookup:`overlaps` X X X X B
:lookup:`relate` X X X C
:lookup:`same_as` X X X X B
:lookup:`touches` X X X X B
:lookup:`within` X X X X B
:lookup:`left` X C
:lookup:`right` X C
:lookup:`overlaps_left` X B
:lookup:`overlaps_right` X B
:lookup:`overlaps_above` X C
:lookup:`overlaps_below` X C
:lookup:`strictly_above` X C
:lookup:`strictly_below` X C
================================= ========= ======== ============ ========== ========
.. _database-functions-compatibility:

View file

@ -11,22 +11,70 @@ GeoQuerySet API Reference
Spatial Lookups
===============
The spatial lookups in this section are available for :class:`GeometryField`.
The spatial lookups in this section are available for :class:`GeometryField`
and :class:`RasterField`.
For an introduction, see the :ref:`spatial lookups introduction
<spatial-lookups-intro>`. For an overview of what lookups are
compatible with a particular spatial backend, refer to the
:ref:`spatial lookup compatibility table <spatial-lookup-compatibility>`.
.. versionchanged:: 1.10
Spatial lookups now support raster input.
Lookups with rasters
--------------------
All examples in the reference below are given for geometry fields and inputs,
but the lookups can be used the same way with rasters on both sides. Whenever
a lookup doesn't support raster input, the input is automatically
converted to a geometry where necessary using the `ST_Polygon
<http://postgis.net/docs/RT_ST_Polygon.html>`_ function. See also the
:ref:`introduction to raster lookups <spatial-lookup-raster>`.
The database operators used by the lookups can be divided into three categories:
- Native raster support ``N``: the operator accepts rasters natively on both
sides of the lookup, and raster input can be mixed with geometry inputs.
- Bilateral raster support ``B``: the operator supports rasters only if both
sides of the lookup receive raster inputs. Raster data is automatically
converted to geometries for mixed lookups.
- Geometry conversion support ``C``. The lookup does not have native raster
support, all raster data is automatically converted to geometries.
The examples below show the SQL equivalent for the lookups in the different
types of raster support. The same pattern applies to all spatial lookups.
==== ============================== =======================================================
Case Lookup SQL Equivalent
==== ============================== =======================================================
N, B ``rast__contains=rst`` ``ST_Contains(rast, rst)``
N, B ``rast__1__contains=(rst, 2)`` ``ST_Contains(rast, 1, rst, 2)``
B, C ``rast__contains=geom`` ``ST_Contains(ST_Polygon(rast), geom)``
B, C ``rast__1__contains=geom`` ``ST_Contains(ST_Polygon(rast, 1), geom)``
B, C ``poly__contains=rst`` ``ST_Contains(poly, ST_Polygon(rst))``
B, C ``poly__contains=(rst, 1)`` ``ST_Contains(poly, ST_Polygon(rst, 1))``
C ``rast__crosses=rst`` ``ST_Crosses(ST_Polygon(rast), ST_Polygon(rst))``
C ``rast__1__crosses=(rst, 2)`` ``ST_Crosses(ST_Polygon(rast, 1), ST_Polygon(rst, 2))``
C ``rast__crosses=geom`` ``ST_Crosses(ST_Polygon(rast), geom)``
C ``poly__crosses=rst`` ``ST_Crosses(poly, ST_Polygon(rst))``
==== ============================== =======================================================
Spatial lookups with rasters are only supported for PostGIS backends
(denominated as PGRaster in this section).
.. fieldlookup:: bbcontains
``bbcontains``
--------------
*Availability*: PostGIS, MySQL, SpatiaLite
*Availability*: PostGIS, MySQL, SpatiaLite, PGRaster (Native)
Tests if the geometry field's bounding box completely contains the lookup
geometry's bounding box.
Tests if the geometry or raster field's bounding box completely contains the
lookup geometry's bounding box.
Example::
@ -45,7 +93,7 @@ SpatiaLite ``MbrContains(poly, geom)``
``bboverlaps``
--------------
*Availability*: PostGIS, MySQL, SpatiaLite
*Availability*: PostGIS, MySQL, SpatiaLite, PGRaster (Native)
Tests if the geometry field's bounding box overlaps the lookup geometry's
bounding box.
@ -67,7 +115,7 @@ SpatiaLite ``MbrOverlaps(poly, geom)``
``contained``
-------------
*Availability*: PostGIS, MySQL, SpatiaLite
*Availability*: PostGIS, MySQL, SpatiaLite, PGRaster (Native)
Tests if the geometry field's bounding box is completely contained by the
lookup geometry's bounding box.
@ -89,7 +137,7 @@ SpatiaLite ``MbrWithin(poly, geom)``
``contains``
------------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Bilateral)
Tests if the geometry field spatially contains the lookup geometry.
@ -111,7 +159,7 @@ SpatiaLite ``Contains(poly, geom)``
``contains_properly``
---------------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Bilateral)
Returns true if the lookup geometry intersects the interior of the
geometry field, but not the boundary (or exterior). [#fncontainsproperly]_
@ -131,7 +179,7 @@ PostGIS ``ST_ContainsProperly(poly, geom)``
``coveredby``
-------------
*Availability*: PostGIS, Oracle
*Availability*: PostGIS, Oracle, PGRaster (Bilateral)
Tests if no point in the geometry field is outside the lookup geometry.
[#fncovers]_
@ -152,7 +200,7 @@ Oracle ``SDO_COVEREDBY(poly, geom)``
``covers``
----------
*Availability*: PostGIS, Oracle
*Availability*: PostGIS, Oracle, PGRaster (Bilateral)
Tests if no point in the lookup geometry is outside the geometry field.
[#fncovers]_
@ -173,7 +221,7 @@ Oracle ``SDO_COVERS(poly, geom)``
``crosses``
-----------
*Availability*: PostGIS, SpatiaLite
*Availability*: PostGIS, SpatiaLite, PGRaster (Conversion)
Tests if the geometry field spatially crosses the lookup geometry.
@ -193,7 +241,7 @@ SpatiaLite ``Crosses(poly, geom)``
``disjoint``
------------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Bilateral)
Tests if the geometry field is spatially disjoint from the lookup geometry.
@ -215,7 +263,7 @@ SpatiaLite ``Disjoint(poly, geom)``
``equals``
----------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Conversion)
.. fieldlookup:: exact
.. fieldlookup:: same_as
@ -223,14 +271,14 @@ SpatiaLite ``Disjoint(poly, geom)``
``exact``, ``same_as``
----------------------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Bilateral)
.. fieldlookup:: intersects
``intersects``
--------------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Bilateral)
Tests if the geometry field spatially intersects the lookup geometry.
@ -271,14 +319,14 @@ PostGIS equivalent::
``overlaps``
------------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Bilateral)
.. fieldlookup:: relate
``relate``
----------
*Availability*: PostGIS, Oracle, SpatiaLite
*Availability*: PostGIS, Oracle, SpatiaLite, PGRaster (Conversion)
Tests if the geometry field is spatially related to the lookup geometry by
the values given in the given pattern. This lookup requires a tuple parameter,
@ -293,7 +341,7 @@ The intersection pattern matrix may only use the following characters:
``1``, ``2``, ``T``, ``F``, or ``*``. This lookup type allows users to "fine tune"
a specific geometric relationship consistent with the DE-9IM model. [#fnde9im]_
Example::
Geometry example::
# A tuple lookup parameter is used to specify the geometry and
# the intersection pattern (the pattern here is for 'contains').
@ -307,6 +355,16 @@ SpatiaLite SQL equivalent::
SELECT ... WHERE Relate(poly, geom, 'T*T***FF*')
Raster example::
Zipcode.objects.filter(poly__relate=(rast, 1, 'T*T***FF*'))
Zipcode.objects.filter(rast__2__relate=(rast, 1, 'T*T***FF*'))
PostGIS SQL equivalent::
SELECT ... WHERE ST_Relate(poly, ST_Polygon(rast, 1), 'T*T***FF*')
SELECT ... WHERE ST_Relate(ST_Polygon(rast, 2), ST_Polygon(rast, 1), 'T*T***FF*')
Oracle
~~~~~~
@ -352,7 +410,7 @@ SpatiaLite ``Touches(poly, geom)``
``within``
----------
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite
*Availability*: PostGIS, Oracle, MySQL, SpatiaLite, PGRaster (Bilateral)
Tests if the geometry field is spatially within the lookup geometry.
@ -374,7 +432,7 @@ SpatiaLite ``Within(poly, geom)``
``left``
--------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Conversion)
Tests if the geometry field's bounding box is strictly to the left of the
lookup geometry's bounding box.
@ -392,7 +450,7 @@ PostGIS equivalent::
``right``
---------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Conversion)
Tests if the geometry field's bounding box is strictly to the right of the
lookup geometry's bounding box.
@ -410,7 +468,7 @@ PostGIS equivalent::
``overlaps_left``
-----------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Bilateral)
Tests if the geometry field's bounding box overlaps or is to the left of the lookup
geometry's bounding box.
@ -429,7 +487,7 @@ PostGIS equivalent::
``overlaps_right``
------------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Bilateral)
Tests if the geometry field's bounding box overlaps or is to the right of the lookup
geometry's bounding box.
@ -447,7 +505,7 @@ PostGIS equivalent::
``overlaps_above``
------------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Conversion)
Tests if the geometry field's bounding box overlaps or is above the lookup
geometry's bounding box.
@ -465,7 +523,7 @@ PostGIS equivalent::
``overlaps_below``
------------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Conversion)
Tests if the geometry field's bounding box overlaps or is below the lookup
geometry's bounding box.
@ -483,7 +541,7 @@ PostGIS equivalent::
``strictly_above``
------------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Conversion)
Tests if the geometry field's bounding box is strictly above the lookup
geometry's bounding box.
@ -501,7 +559,7 @@ PostGIS equivalent::
``strictly_below``
------------------
*Availability*: PostGIS
*Availability*: PostGIS, PGRaster (Conversion)
Tests if the geometry field's bounding box is strictly below the lookup
geometry's bounding box.
@ -520,27 +578,31 @@ PostGIS equivalent::
Distance Lookups
================
*Availability*: PostGIS, Oracle, SpatiaLite
*Availability*: PostGIS, Oracle, SpatiaLite, PGRaster (Native)
For an overview on performing distance queries, please refer to
the :ref:`distance queries introduction <distance-queries>`.
Distance lookups take the following form::
<field>__<distance lookup>=(<geometry>, <distance value>[, 'spheroid'])
<field>__<distance lookup>=(<geometry/raster>, <distance value>[, 'spheroid'])
<field>__<distance lookup>=(<raster>, <band_index>, <distance value>[, 'spheroid'])
<field>__<band_index>__<distance lookup>=(<raster>, <band_index>, <distance value>[, 'spheroid'])
The value passed into a distance lookup is a tuple; the first two
values are mandatory, and are the geometry to calculate distances to,
and a distance value (either a number in units of the field, a
:class:`~django.contrib.gis.measure.Distance` object, or a `query expression
<ref/models/expressions>`).
<ref/models/expressions>`). To pass a band index to the lookup, use a 3-tuple
where the second entry is the band index.
With PostGIS, on every distance lookup but :lookup:`dwithin`, an optional
third element, ``'spheroid'``, may be included to tell GeoDjango
to use the more accurate spheroid distance calculation functions on
fields with a geodetic coordinate system (e.g., ``ST_Distance_Spheroid``
would be used instead of ``ST_Distance_Sphere``). The simpler ``ST_Distance``
function is used with projected coordinate systems.
element, ``'spheroid'``, may be included to tell GeoDjango to use the more
accurate spheroid distance calculation functions on fields with a geodetic
coordinate system (e.g., ``ST_Distance_Spheroid`` would be used instead of
``ST_Distance_Sphere``). The simpler ``ST_Distance`` function is used with
projected coordinate systems. Rasters are converted to geometries for spheroid
based lookups.
.. versionadded:: 1.10