====================== 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`` .. module:: django.contrib.gis.db.models :synopsis: GeoDjango's database API. .. _mysql-spatial-limitations: MySQL Spatial Limitations ------------------------- MySQL's spatial extensions only support bounding box operations (what MySQL calls minimum bounding rectangles, or MBR). Specifically, `MySQL does not conform to the OGC standard `_: Currently, MySQL does not implement these functions [``Contains``, ``Crosses``, ``Disjoint``, ``Intersects``, ``Overlaps``, ``Touches``, ``Within``] according to the specification. Those that are implemented return the same result as the corresponding MBR-based functions. In other words, while spatial lookups such as :lookup:`contains ` are available in GeoDjango when using MySQL, the results returned are really equivalent to what would be returned when using :lookup:`bbcontains` on a different spatial backend. .. warning:: True spatial indexes (R-trees) are only supported with MyISAM tables on MySQL. [#fnmysqlidx]_ In other words, when using MySQL spatial extensions you have to choose between fast spatial lookups and the integrity of your data -- MyISAM tables do not support transactions or foreign key constraints. 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. Creating and Saving Models with Geometry Fields =============================================== Here is an example of how to create a geometry object (assuming the ``Zipcode`` model):: >>> 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:: >>> 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:: >>> 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 [#fngeojson]_ (requires GDAL). 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 `. .. _creating-and-saving-raster-models: Creating and Saving Models with Raster Fields ============================================= .. versionadded:: 1.9 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):: >>> 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:: >>> 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:: >>> 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 geometry 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`):: >>> qs = Zipcode.objects.filter(__=) >>> qs = Zipcode.objects.exclude(...) For example:: >>> qs = Zipcode.objects.filter(poly__contains=pnt) In this case, ``poly`` is the geographic field, :lookup:`contains ` is the spatial lookup type, and ``pnt`` is the parameter (which may be a :class:`~django.contrib.gis.geos.GEOSGeometry` object or a string of GeoJSON , WKT, or HEXEWKB). A complete reference can be found in the :ref:`spatial lookup reference `. .. _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, Oracle, SpatiaLite The following distance lookups are available: * :lookup:`distance_lt` * :lookup:`distance_lte` * :lookup:`distance_gt` * :lookup:`distance_gte` * :lookup:`dwithin` .. 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 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 ` 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 `GeoDjango distance tests`__ ) 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:: >>> 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))) __ https://github.com/django/django/blob/master/tests/gis_tests/distapp/models.py .. _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. ================================= ========= ======== ============ ========== Lookup Type PostGIS Oracle MySQL [#]_ SpatiaLite ================================= ========= ======== ============ ========== :lookup:`bbcontains` X X X :lookup:`bboverlaps` X X X :lookup:`contained` X X X :lookup:`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:`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 ================================= ========= ======== ============ ========== .. _database-functions-compatibility: Database functions ------------------ .. module:: django.contrib.gis.db.models.functions :synopsis: GeoDjango's database functions. The following table provides a summary of what geography-specific database functions are available on each spatial backend. ==================================== ======= ====== =========== ========== Function PostGIS Oracle MySQL SpatiaLite ==================================== ======= ====== =========== ========== :class:`Area` X X X X :class:`AsGeoJSON` X X :class:`AsGML` X X X :class:`AsKML` X X :class:`AsSVG` X X :class:`BoundingCircle` X :class:`Centroid` X X X X :class:`Difference` X X X (≥ 5.6.1) X :class:`Distance` X X X (≥ 5.6.1) X :class:`Envelope` X X X :class:`ForceRHR` X :class:`GeoHash` X X (≥ 4.0, LWGEOM) :class:`Intersection` X X X (≥ 5.6.1) X :class:`IsValid` X :class:`Length` X X X X :class:`MakeValid` X :class:`MemSize` X :class:`NumGeometries` X X X X :class:`NumPoints` X X X X :class:`Perimeter` X X X (≥ 4.0) :class:`PointOnSurface` X X X :class:`Reverse` X X X (≥ 4.0) :class:`Scale` X X :class:`SnapToGrid` X X (≥ 3.1) :class:`SymDifference` X X X (≥ 5.6.1) X :class:`Transform` X X X :class:`Translate` X X :class:`Union` X X X (≥ 5.6.1) X ==================================== ======= ====== =========== ========== Aggregate Functions ------------------- The following table provides a summary of what GIS-specific aggregate functions are available on each spatial backend. Please note that MySQL does not support any of these aggregates, and is thus excluded from the table. .. currentmodule:: django.contrib.gis.db.models ======================= ======= ====== ========== Aggregate PostGIS Oracle SpatiaLite ======================= ======= ====== ========== :class:`Collect` X 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 `_, 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 `_, PostGIS documentation at Ch. 4.1.2. .. [#fngeojson] *See* Howard Butler, Martin Daly, Allan Doyle, Tim Schaub, & Christopher Schmidt, `The GeoJSON Format Specification `_, Revision 1.0 (June 16, 2008). .. [#fndistsphere15] *See* `PostGIS documentation `_ on ``ST_distance_sphere``. .. [#fnmysqlidx] *See* `Creating Spatial Indexes `_ in the MySQL Reference Manual: For MyISAM tables, ``SPATIAL INDEX`` creates an R-tree index. For storage engines that support nonspatial indexing of spatial columns, the engine creates a B-tree index. A B-tree index on spatial values will be useful for exact-value lookups, but not for range scans. .. [#] Refer :ref:`mysql-spatial-limitations` section for more details.