Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

hemamaps / Django   python

Repository URL to install this package:

/ contrib / gis / db / models / query.py

import warnings

from django.contrib.gis.db.models import aggregates
from django.contrib.gis.db.models.fields import (
    GeometryField, LineStringField, PointField, get_srid_info,
)
from django.contrib.gis.db.models.lookups import GISLookup
from django.contrib.gis.db.models.sql import (
    AreaField, DistanceField, GeomField, GMLField,
)
from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Area, Distance
from django.db import connections
from django.db.models.expressions import RawSQL
from django.db.models.fields import Field
from django.db.models.query import QuerySet
from django.utils import six
from django.utils.deprecation import (
    RemovedInDjango20Warning, RemovedInDjango110Warning,
)


class GeoQuerySet(QuerySet):
    "The Geographic QuerySet."

    # ### GeoQuerySet Methods ###
    def area(self, tolerance=0.05, **kwargs):
        """
        Returns the area of the geographic field in an `area` attribute on
        each element of this GeoQuerySet.
        """
        # Performing setup here rather than in `_spatial_attribute` so that
        # we can get the units for `AreaField`.
        procedure_args, geo_field = self._spatial_setup(
            'area', field_name=kwargs.get('field_name'))
        s = {'procedure_args': procedure_args,
             'geo_field': geo_field,
             'setup': False,
             }
        connection = connections[self.db]
        backend = connection.ops
        if backend.oracle:
            s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
            s['procedure_args']['tolerance'] = tolerance
            s['select_field'] = AreaField('sq_m')  # Oracle returns area in units of meters.
        elif backend.postgis or backend.spatialite:
            if backend.geography:
                # Geography fields support area calculation, returns square meters.
                s['select_field'] = AreaField('sq_m')
            elif not geo_field.geodetic(connection):
                # Getting the area units of the geographic field.
                s['select_field'] = AreaField(Area.unit_attname(geo_field.units_name(connection)))
            else:
                # TODO: Do we want to support raw number areas for geodetic fields?
                raise Exception('Area on geodetic coordinate systems not supported.')
        return self._spatial_attribute('area', s, **kwargs)

    def centroid(self, **kwargs):
        """
        Returns the centroid of the geographic field in a `centroid`
        attribute on each element of this GeoQuerySet.
        """
        return self._geom_attribute('centroid', **kwargs)

    def collect(self, **kwargs):
        """
        Performs an aggregate collect operation on the given geometry field.
        This is analogous to a union operation, but much faster because
        boundaries are not dissolved.
        """
        warnings.warn(
            "The collect GeoQuerySet method is deprecated. Use the Collect() "
            "aggregate in an aggregate() or annotate() method.",
            RemovedInDjango110Warning, stacklevel=2
        )
        return self._spatial_aggregate(aggregates.Collect, **kwargs)

    def difference(self, geom, **kwargs):
        """
        Returns the spatial difference of the geographic field in a `difference`
        attribute on each element of this GeoQuerySet.
        """
        return self._geomset_attribute('difference', geom, **kwargs)

    def distance(self, geom, **kwargs):
        """
        Returns the distance from the given geographic field name to the
        given geometry in a `distance` attribute on each element of the
        GeoQuerySet.

        Keyword Arguments:
         `spheroid`  => If the geometry field is geodetic and PostGIS is
                        the spatial database, then the more accurate
                        spheroid calculation will be used instead of the
                        quicker sphere calculation.

         `tolerance` => Used only for Oracle. The tolerance is
                        in meters -- a default of 5 centimeters (0.05)
                        is used.
        """
        return self._distance_attribute('distance', geom, **kwargs)

    def envelope(self, **kwargs):
        """
        Returns a Geometry representing the bounding box of the
        Geometry field in an `envelope` attribute on each element of
        the GeoQuerySet.
        """
        return self._geom_attribute('envelope', **kwargs)

    def extent(self, **kwargs):
        """
        Returns the extent (aggregate) of the features in the GeoQuerySet.  The
        extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax).
        """
        warnings.warn(
            "The extent GeoQuerySet method is deprecated. Use the Extent() "
            "aggregate in an aggregate() or annotate() method.",
            RemovedInDjango110Warning, stacklevel=2
        )
        return self._spatial_aggregate(aggregates.Extent, **kwargs)

    def extent3d(self, **kwargs):
        """
        Returns the aggregate extent, in 3D, of the features in the
        GeoQuerySet. It is returned as a 6-tuple, comprising:
          (xmin, ymin, zmin, xmax, ymax, zmax).
        """
        warnings.warn(
            "The extent3d GeoQuerySet method is deprecated. Use the Extent3D() "
            "aggregate in an aggregate() or annotate() method.",
            RemovedInDjango110Warning, stacklevel=2
        )
        return self._spatial_aggregate(aggregates.Extent3D, **kwargs)

    def force_rhr(self, **kwargs):
        """
        Returns a modified version of the Polygon/MultiPolygon in which
        all of the vertices follow the Right-Hand-Rule.  By default,
        this is attached as the `force_rhr` attribute on each element
        of the GeoQuerySet.
        """
        return self._geom_attribute('force_rhr', **kwargs)

    def geojson(self, precision=8, crs=False, bbox=False, **kwargs):
        """
        Returns a GeoJSON representation of the geometry field in a `geojson`
        attribute on each element of the GeoQuerySet.

        The `crs` and `bbox` keywords may be set to True if the user wants
        the coordinate reference system and the bounding box to be included
        in the GeoJSON representation of the geometry.
        """
        backend = connections[self.db].ops
        if not backend.geojson:
            raise NotImplementedError('Only PostGIS 1.3.4+ and SpatiaLite 3.0+ '
                                      'support GeoJSON serialization.')

        if not isinstance(precision, six.integer_types):
            raise TypeError('Precision keyword must be set with an integer.')

        options = 0
        if crs and bbox:
            options = 3
        elif bbox:
            options = 1
        elif crs:
            options = 2
        s = {'desc': 'GeoJSON',
             'procedure_args': {'precision': precision, 'options': options},
             'procedure_fmt': '%(geo_col)s,%(precision)s,%(options)s',
             }
        return self._spatial_attribute('geojson', s, **kwargs)

    def geohash(self, precision=20, **kwargs):
        """
        Returns a GeoHash representation of the given field in a `geohash`
        attribute on each element of the GeoQuerySet.

        The `precision` keyword may be used to custom the number of
        _characters_ used in the output GeoHash, the default is 20.
        """
        s = {'desc': 'GeoHash',
             'procedure_args': {'precision': precision},
             'procedure_fmt': '%(geo_col)s,%(precision)s',
             }
        return self._spatial_attribute('geohash', s, **kwargs)

    def gml(self, precision=8, version=2, **kwargs):
        """
        Returns GML representation of the given field in a `gml` attribute
        on each element of the GeoQuerySet.
        """
        backend = connections[self.db].ops
        s = {'desc': 'GML', 'procedure_args': {'precision': precision}}
        if backend.postgis:
            s['procedure_fmt'] = '%(version)s,%(geo_col)s,%(precision)s'
            s['procedure_args'] = {'precision': precision, 'version': version}
        if backend.oracle:
            s['select_field'] = GMLField()

        return self._spatial_attribute('gml', s, **kwargs)

    def intersection(self, geom, **kwargs):
        """
        Returns the spatial intersection of the Geometry field in
        an `intersection` attribute on each element of this
        GeoQuerySet.
        """
        return self._geomset_attribute('intersection', geom, **kwargs)

    def kml(self, **kwargs):
        """
        Returns KML representation of the geometry field in a `kml`
        attribute on each element of this GeoQuerySet.
        """
        s = {'desc': 'KML',
             'procedure_fmt': '%(geo_col)s,%(precision)s',
             'procedure_args': {'precision': kwargs.pop('precision', 8)},
             }
        return self._spatial_attribute('kml', s, **kwargs)

    def length(self, **kwargs):
        """
        Returns the length of the geometry field as a `Distance` object
        stored in a `length` attribute on each element of this GeoQuerySet.
        """
        return self._distance_attribute('length', None, **kwargs)

    def make_line(self, **kwargs):
        """
        Creates a linestring from all of the PointField geometries in the
        this GeoQuerySet and returns it.  This is a spatial aggregate
        method, and thus returns a geometry rather than a GeoQuerySet.
        """
        warnings.warn(
            "The make_line GeoQuerySet method is deprecated. Use the MakeLine() "
            "aggregate in an aggregate() or annotate() method.",
            RemovedInDjango110Warning, stacklevel=2
        )
        return self._spatial_aggregate(aggregates.MakeLine, geo_field_type=PointField, **kwargs)

    def mem_size(self, **kwargs):
        """
        Returns the memory size (number of bytes) that the geometry field takes
        in a `mem_size` attribute  on each element of this GeoQuerySet.
        """
        return self._spatial_attribute('mem_size', {}, **kwargs)

    def num_geom(self, **kwargs):
        """
        Returns the number of geometries if the field is a
        GeometryCollection or Multi* Field in a `num_geom`
        attribute on each element of this GeoQuerySet; otherwise
        the sets with None.
        """
        return self._spatial_attribute('num_geom', {}, **kwargs)

    def num_points(self, **kwargs):
        """
        Returns the number of points in the first linestring in the
        Geometry field in a `num_points` attribute on each element of
        this GeoQuerySet; otherwise sets with None.
        """
        return self._spatial_attribute('num_points', {}, **kwargs)

    def perimeter(self, **kwargs):
        """
        Returns the perimeter of the geometry field as a `Distance` object
        stored in a `perimeter` attribute on each element of this GeoQuerySet.
        """
        return self._distance_attribute('perimeter', None, **kwargs)

    def point_on_surface(self, **kwargs):
        """
        Returns a Point geometry guaranteed to lie on the surface of the
        Geometry field in a `point_on_surface` attribute on each element
        of this GeoQuerySet; otherwise sets with None.
        """
        return self._geom_attribute('point_on_surface', **kwargs)

    def reverse_geom(self, **kwargs):
        """
        Reverses the coordinate order of the geometry, and attaches as a
        `reverse` attribute on each element of this GeoQuerySet.
        """
        s = {'select_field': GeomField()}
        kwargs.setdefault('model_att', 'reverse_geom')
        if connections[self.db].ops.oracle:
            s['geo_field_type'] = LineStringField
        return self._spatial_attribute('reverse', s, **kwargs)

    def scale(self, x, y, z=0.0, **kwargs):
        """
        Scales the geometry to a new size by multiplying the ordinates
        with the given x,y,z scale factors.
        """
        if connections[self.db].ops.spatialite:
            if z != 0.0:
                raise NotImplementedError('SpatiaLite does not support 3D scaling.')
            s = {'procedure_fmt': '%(geo_col)s,%(x)s,%(y)s',
                 'procedure_args': {'x': x, 'y': y},
                 'select_field': GeomField(),
                 }
        else:
            s = {'procedure_fmt': '%(geo_col)s,%(x)s,%(y)s,%(z)s',
                 'procedure_args': {'x': x, 'y': y, 'z': z},
                 'select_field': GeomField(),
                 }
        return self._spatial_attribute('scale', s, **kwargs)

    def snap_to_grid(self, *args, **kwargs):
        """
        Snap all points of the input geometry to the grid.  How the
        geometry is snapped to the grid depends on how many arguments
        were given:
          - 1 argument : A single size to snap both the X and Y grids to.
          - 2 arguments: X and Y sizes to snap the grid to.
          - 4 arguments: X, Y sizes and the X, Y origins.
        """
        if False in [isinstance(arg, (float,) + six.integer_types) for arg in args]:
            raise TypeError('Size argument(s) for the grid must be a float or integer values.')

        nargs = len(args)
        if nargs == 1:
            size = args[0]
            procedure_fmt = '%(geo_col)s,%(size)s'
            procedure_args = {'size': size}
        elif nargs == 2:
            xsize, ysize = args
            procedure_fmt = '%(geo_col)s,%(xsize)s,%(ysize)s'
            procedure_args = {'xsize': xsize, 'ysize': ysize}
        elif nargs == 4:
            xsize, ysize, xorigin, yorigin = args
            procedure_fmt = '%(geo_col)s,%(xorigin)s,%(yorigin)s,%(xsize)s,%(ysize)s'
            procedure_args = {'xsize': xsize, 'ysize': ysize,
                              'xorigin': xorigin, 'yorigin': yorigin}
        else:
            raise ValueError('Must provide 1, 2, or 4 arguments to `snap_to_grid`.')

        s = {'procedure_fmt': procedure_fmt,
             'procedure_args': procedure_args,
             'select_field': GeomField(),
             }
Loading ...