123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- ======================
- GeoDjango Database API
- ======================
- .. _spatial-backends:
- Spatial Backends
- ================
- .. module:: django.contrib.gis.db.backends
- :synopsis: GeoDjango's spatial database backends.
- GeoDjango currently provides the following spatial database backends:
- * ``django.contrib.gis.db.backends.postgis``
- * ``django.contrib.gis.db.backends.mysql``
- * ``django.contrib.gis.db.backends.oracle``
- * ``django.contrib.gis.db.backends.spatialite``
- .. _mysql-spatial-limitations:
- MySQL Spatial Limitations
- -------------------------
- Django supports spatial functions operating on real geometries available in
- modern MySQL versions. However, the spatial functions are not as rich as other
- backends like PostGIS.
- Raster Support
- --------------
- ``RasterField`` is currently only implemented for the PostGIS backend. Spatial
- lookups are available for raster fields, but spatial database functions and
- aggregates aren't implemented for raster fields.
- Creating and Saving Models with Geometry Fields
- ===============================================
- Here is an example of how to create a geometry object (assuming the ``Zipcode``
- model):
- .. code-block:: pycon
- >>> from zipcode.models import Zipcode
- >>> z = Zipcode(code=77096, poly="POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))")
- >>> z.save()
- :class:`~django.contrib.gis.geos.GEOSGeometry` objects may also be used to save geometric models:
- .. code-block:: pycon
- >>> from django.contrib.gis.geos import GEOSGeometry
- >>> poly = GEOSGeometry("POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))")
- >>> z = Zipcode(code=77096, poly=poly)
- >>> z.save()
- Moreover, if the ``GEOSGeometry`` is in a different coordinate system (has a
- different SRID value) than that of the field, then it will be implicitly
- transformed into the SRID of the model's field, using the spatial database's
- transform procedure:
- .. code-block:: pycon
- >>> poly_3084 = GEOSGeometry(
- ... "POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))", srid=3084
- ... ) # SRID 3084 is 'NAD83(HARN) / Texas Centric Lambert Conformal'
- >>> z = Zipcode(code=78212, poly=poly_3084)
- >>> z.save()
- >>> from django.db import connection
- >>> print(
- ... connection.queries[-1]["sql"]
- ... ) # printing the last SQL statement executed (requires DEBUG=True)
- INSERT INTO "geoapp_zipcode" ("code", "poly") VALUES (78212, ST_Transform(ST_GeomFromWKB('\\001 ... ', 3084), 4326))
- Thus, geometry parameters may be passed in using the ``GEOSGeometry`` object, WKT
- (Well Known Text [#fnwkt]_), HEXEWKB (PostGIS specific -- a WKB geometry in
- hexadecimal [#fnewkb]_), and GeoJSON (see :rfc:`7946`). Essentially, if the
- input is not a ``GEOSGeometry`` object, the geometry field will attempt to
- create a ``GEOSGeometry`` instance from the input.
- For more information creating :class:`~django.contrib.gis.geos.GEOSGeometry`
- objects, refer to the :ref:`GEOS tutorial <geos-tutorial>`.
- .. _creating-and-saving-raster-models:
- Creating and Saving Models with Raster Fields
- =============================================
- When creating raster models, the raster field will implicitly convert the input
- into a :class:`~django.contrib.gis.gdal.GDALRaster` using lazy-evaluation.
- The raster field will therefore accept any input that is accepted by the
- :class:`~django.contrib.gis.gdal.GDALRaster` constructor.
- Here is an example of how to create a raster object from a raster file
- ``volcano.tif`` (assuming the ``Elevation`` model):
- .. code-block:: pycon
- >>> from elevation.models import Elevation
- >>> dem = Elevation(name="Volcano", rast="/path/to/raster/volcano.tif")
- >>> dem.save()
- :class:`~django.contrib.gis.gdal.GDALRaster` objects may also be used to save
- raster models:
- .. code-block:: pycon
- >>> from django.contrib.gis.gdal import GDALRaster
- >>> rast = GDALRaster(
- ... {
- ... "width": 10,
- ... "height": 10,
- ... "name": "Canyon",
- ... "srid": 4326,
- ... "scale": [0.1, -0.1],
- ... "bands": [{"data": range(100)}],
- ... }
- ... )
- >>> dem = Elevation(name="Canyon", rast=rast)
- >>> dem.save()
- Note that this equivalent to:
- .. code-block:: pycon
- >>> dem = Elevation.objects.create(
- ... name="Canyon",
- ... rast={
- ... "width": 10,
- ... "height": 10,
- ... "name": "Canyon",
- ... "srid": 4326,
- ... "scale": [0.1, -0.1],
- ... "bands": [{"data": range(100)}],
- ... },
- ... )
- .. _spatial-lookups-intro:
- 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 spatial fields.
- Filters on 'normal' fields (e.g. :class:`~django.db.models.CharField`)
- 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`):
- .. code-block:: text
- >>> qs = Zipcode.objects.filter(<field>__<lookup_type>=<parameter>)
- >>> qs = Zipcode.objects.exclude(...)
- For example:
- .. code-block:: pycon
- >>> 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, ``pnt`` is the parameter (which may be a
- :class:`~django.contrib.gis.geos.GEOSGeometry` object or a string of
- GeoJSON , WKT, or HEXEWKB), and ``rst`` is a
- :class:`~django.contrib.gis.gdal.GDALRaster` object.
- .. _spatial-lookup-raster:
- Raster Lookups
- --------------
- The raster lookup syntax is similar to the syntax for geometries. The only
- difference is that a band index can be 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`):
- .. code-block:: text
- >>> 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>)
- For example:
- .. code-block:: pycon
- >>> 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:
- Distance Queries
- ================
- Introduction
- ------------
- Distance calculations with spatial data is tricky because, unfortunately,
- the Earth is not flat. Some distance queries with fields in a geographic
- coordinate system may have to be expressed differently because of
- limitations in PostGIS. Please see the :ref:`selecting-an-srid` section
- in the :doc:`model-api` documentation for more details.
- .. _distance-lookups-intro:
- Distance Lookups
- ----------------
- *Availability*: PostGIS, MariaDB, MySQL, Oracle, SpatiaLite, PGRaster (Native)
- The following distance lookups are available:
- * :lookup:`distance_lt`
- * :lookup:`distance_lte`
- * :lookup:`distance_gt`
- * :lookup:`distance_gte`
- * :lookup:`dwithin` (except MariaDB and MySQL)
- .. note::
- For *measuring*, rather than querying on distances, use the
- :class:`~django.contrib.gis.db.models.functions.Distance` function.
- Distance lookups take a tuple parameter comprising:
- #. 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,
- it may be expressed in any units (the SQL generated will use units
- converted to those of the field); otherwise, numeric parameters are assumed
- to be in the units of the field.
- .. note::
- In PostGIS, ``ST_Distance_Sphere`` does *not* limit the geometry types
- geographic distance queries are performed with. [#fndistsphere15]_ However,
- these queries may take a long time, as great-circle distances must be
- calculated on the fly for *every* row in the query. This is because the
- 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
- :source:`GeoDjango distance tests <tests/gis_tests/distapp/models.py>` ) on a
- *projected* coordinate system valid for cities in southern Texas::
- from django.contrib.gis.db import models
- class SouthTexasCity(models.Model):
- name = models.CharField(max_length=30)
- # A projected coordinate system (only valid for South Texas!)
- # is used, units are in meters.
- point = models.PointField(srid=32140)
- Then distance queries may be performed as follows:
- .. code-block:: pycon
- >>> from django.contrib.gis.geos import GEOSGeometry
- >>> from django.contrib.gis.measure import D # ``D`` is a shortcut for ``Distance``
- >>> from geoapp.models import SouthTexasCity
- # Distances will be calculated from this point, which does not have to be projected.
- >>> pnt = GEOSGeometry("POINT(-96.876369 29.905320)", srid=4326)
- # If numeric parameter, units of field (meters in this case) are assumed.
- >>> qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, 7000))
- # Find all Cities within 7 km, > 20 miles away, and > 100 chains away (an obscure unit)
- >>> qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, D(km=7)))
- >>> 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 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:
- .. code-block:: pycon
- >>> 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.
- .. _compatibility-table:
- Compatibility Tables
- ====================
- .. _spatial-lookup-compatibility:
- Spatial Lookups
- ---------------
- The following table provides a summary of what spatial lookups are available
- 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 MariaDB MySQL [#]_ SpatiaLite PGRaster
- ================================= ========= ======== ========== ============ ========== ========
- :lookup:`bbcontains` X X X X N
- :lookup:`bboverlaps` X X X X N
- :lookup:`contained` X X X X N
- :lookup:`contains <gis-contains>` X X X X X B
- :lookup:`contains_properly` X B
- :lookup:`coveredby` X X X X B
- :lookup:`covers` X X X X B
- :lookup:`crosses` X X X X C
- :lookup:`disjoint` X X X X X B
- :lookup:`distance_gt` X X X X X N
- :lookup:`distance_gte` X X X X X N
- :lookup:`distance_lt` X X X X X N
- :lookup:`distance_lte` X X X X X N
- :lookup:`dwithin` X X X B
- :lookup:`equals` X X X X X C
- :lookup:`exact <same_as>` X X X X X B
- :lookup:`intersects` X X X X X B
- :lookup:`isempty` X
- :lookup:`isvalid` X X X X
- :lookup:`overlaps` X X X X X B
- :lookup:`relate` X X X X C
- :lookup:`same_as` X X X X X B
- :lookup:`touches` X X X X X B
- :lookup:`within` X 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:
- Database functions
- ------------------
- The following table provides a summary of what geography-specific database
- functions are available on each spatial backend.
- .. currentmodule:: django.contrib.gis.db.models.functions
- ==================================== ======= ============== ============ =========== =================
- Function PostGIS Oracle MariaDB MySQL SpatiaLite
- ==================================== ======= ============== ============ =========== =================
- :class:`Area` X X X X X
- :class:`AsGeoJSON` X X X X X
- :class:`AsGML` X X X
- :class:`AsKML` X X
- :class:`AsSVG` X X
- :class:`AsWKB` X X X X X
- :class:`AsWKT` X X X X X
- :class:`Azimuth` X X (LWGEOM/RTTOPO)
- :class:`BoundingCircle` X X X (≥ 5.1)
- :class:`Centroid` X X X X X
- :class:`ClosestPoint` X X
- :class:`Difference` X X X X X
- :class:`Distance` X X X X X
- :class:`Envelope` X X X X X
- :class:`ForcePolygonCW` X X
- :class:`FromWKB` X X X X X
- :class:`FromWKT` X X X X X
- :class:`GeoHash` X X X (LWGEOM/RTTOPO)
- :class:`GeometryDistance` X
- :class:`Intersection` X X X X X
- :class:`IsEmpty` X
- :class:`IsValid` X X X X
- :class:`Length` X X X X X
- :class:`LineLocatePoint` X X
- :class:`MakeValid` X X (LWGEOM/RTTOPO)
- :class:`MemSize` X
- :class:`NumGeometries` X X X X X
- :class:`NumPoints` X X X X X
- :class:`Perimeter` X X X
- :class:`PointOnSurface` X X X X
- :class:`Reverse` X X X
- :class:`Scale` X X
- :class:`SnapToGrid` X X
- :class:`SymDifference` X X X X X
- :class:`Transform` X X X
- :class:`Translate` X X
- :class:`Union` X X X X X
- ==================================== ======= ============== ============ =========== =================
- Aggregate Functions
- -------------------
- The following table provides a summary of what GIS-specific aggregate functions
- are available on each spatial backend. Please note that MariaDB does not
- support any of these aggregates, and is thus excluded from the table.
- .. currentmodule:: django.contrib.gis.db.models
- ======================= ======= ====== ============ ==========
- Aggregate PostGIS Oracle MySQL SpatiaLite
- ======================= ======= ====== ============ ==========
- :class:`Collect` X X (≥ 8.0.24) X
- :class:`Extent` X X X
- :class:`Extent3D` X
- :class:`MakeLine` X X
- :class:`Union` X X X
- ======================= ======= ====== ============ ==========
- .. rubric:: Footnotes
- .. [#fnwkt] *See* Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <https://portal.ogc.org/files/?artifact_id=829>`_, 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 <https://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT>`_, PostGIS documentation at Ch. 4.1.2.
- .. [#fndistsphere15] *See* `PostGIS documentation <https://postgis.net/docs/ST_DistanceSphere.html>`_ on ``ST_DistanceSphere``.
- .. [#] Refer :ref:`mysql-spatial-limitations` section for more details.
|