Removed GeoManager and GeoQuerySet per deprecation timeline.
This commit is contained in:
parent
e90c745afd
commit
a0d166306f
|
@ -1,5 +1,4 @@
|
|||
import re
|
||||
from functools import partial
|
||||
|
||||
from django.contrib.gis.db.models import aggregates
|
||||
|
||||
|
@ -71,17 +70,6 @@ class BaseSpatialFeatures(object):
|
|||
def supports_isvalid_lookup(self):
|
||||
return 'isvalid' in self.connection.ops.gis_operators
|
||||
|
||||
# For each of those methods, the class will have a property named
|
||||
# `has_<name>_method` (defined in __init__) which accesses connection.ops
|
||||
# to determine GIS method availability.
|
||||
geoqueryset_methods = (
|
||||
'area', 'bounding_circle', 'centroid', 'difference', 'distance',
|
||||
'distance_spheroid', 'envelope', 'force_rhr', 'geohash', 'gml',
|
||||
'intersection', 'kml', 'length', 'mem_size', 'num_geom', 'num_points',
|
||||
'perimeter', 'point_on_surface', 'reverse', 'scale', 'snap_to_grid',
|
||||
'svg', 'sym_difference', 'transform', 'translate', 'union', 'unionagg',
|
||||
)
|
||||
|
||||
# Is the aggregate supported by the database?
|
||||
@property
|
||||
def supports_collect_aggr(self):
|
||||
|
@ -99,19 +87,9 @@ class BaseSpatialFeatures(object):
|
|||
def supports_union_aggr(self):
|
||||
return aggregates.Union not in self.connection.ops.disallowed_aggregates
|
||||
|
||||
def __init__(self, *args):
|
||||
super(BaseSpatialFeatures, self).__init__(*args)
|
||||
for method in self.geoqueryset_methods:
|
||||
# Add dynamically properties for each GQS method, e.g. has_force_rhr_method, etc.
|
||||
setattr(self.__class__, 'has_%s_method' % method,
|
||||
property(partial(BaseSpatialFeatures.has_ops_method, method=method)))
|
||||
|
||||
def __getattr__(self, name):
|
||||
m = re.match(r'has_(\w*)_function$', name)
|
||||
if m:
|
||||
func_name = m.group(1)
|
||||
return func_name not in self.connection.ops.unsupported_functions
|
||||
raise AttributeError
|
||||
|
||||
def has_ops_method(self, method):
|
||||
return getattr(self.connection.ops, method, False)
|
||||
|
|
|
@ -55,7 +55,6 @@ class OracleSpatialRefSys(models.Model, SpatialRefSysMixin):
|
|||
# Optional geometry representing the bounds of this coordinate
|
||||
# system. By default, all are NULL in the table.
|
||||
cs_bounds = models.PolygonField(null=True)
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'gis'
|
||||
|
|
|
@ -7,11 +7,10 @@ from django.contrib.gis.db.models.fields import (
|
|||
MultiLineStringField, MultiPointField, MultiPolygonField, PointField,
|
||||
PolygonField, RasterField,
|
||||
)
|
||||
from django.contrib.gis.db.models.manager import GeoManager
|
||||
|
||||
__all__ = models_all + aggregates_all
|
||||
__all__ += [
|
||||
'GeometryCollectionField', 'GeometryField', 'LineStringField',
|
||||
'MultiLineStringField', 'MultiPointField', 'MultiPolygonField', 'PointField',
|
||||
'PolygonField', 'RasterField', 'GeoManager',
|
||||
'PolygonField', 'RasterField',
|
||||
]
|
||||
|
|
|
@ -346,27 +346,6 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField):
|
|||
defaults['widget'] = forms.Textarea
|
||||
return super(GeometryField, self).formfield(**defaults)
|
||||
|
||||
def _get_db_prep_lookup(self, lookup_type, value, connection):
|
||||
"""
|
||||
Prepare for the database lookup, and return any spatial parameters
|
||||
necessary for the query. This includes wrapping any geometry
|
||||
parameters with a backend-specific adapter and formatting any distance
|
||||
parameters into the correct units for the coordinate system of the
|
||||
field.
|
||||
|
||||
Only used by the deprecated GeoQuerySet and to be
|
||||
RemovedInDjango20Warning.
|
||||
"""
|
||||
# Populating the parameters list, and wrapping the Geometry
|
||||
# with the Adapter of the spatial backend.
|
||||
if isinstance(value, (tuple, list)):
|
||||
params = [connection.ops.Adapter(value[0])]
|
||||
# Getting the distance parameter in the units of the field.
|
||||
params += self.get_distance(value[1:], lookup_type, connection)
|
||||
else:
|
||||
params = [connection.ops.Adapter(value)]
|
||||
return params
|
||||
|
||||
|
||||
# The OpenGIS Geometry Type Fields
|
||||
class PointField(GeometryField):
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import warnings
|
||||
|
||||
from django.contrib.gis.db.models.query import GeoQuerySet
|
||||
from django.db.models.manager import Manager
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
|
||||
|
||||
class GeoManager(Manager.from_queryset(GeoQuerySet)):
|
||||
"Overrides Manager to return Geographic QuerySets."
|
||||
|
||||
# This manager should be used for queries on related fields
|
||||
# so that geometry columns on Oracle and MySQL are selected
|
||||
# properly.
|
||||
use_for_related_fields = True
|
||||
|
||||
# No need to bother users with the use_for_related_fields
|
||||
# deprecation for this manager which is itself deprecated.
|
||||
silence_use_for_related_fields_deprecation = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn(
|
||||
"The GeoManager class is deprecated. Simply use a normal manager "
|
||||
"once you have replaced all calls to GeoQuerySet methods by annotations.",
|
||||
RemovedInDjango20Warning, stacklevel=2
|
||||
)
|
||||
super(GeoManager, self).__init__(*args, **kwargs)
|
|
@ -1,727 +0,0 @@
|
|||
import warnings
|
||||
|
||||
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.constants import LOOKUP_SEP
|
||||
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
|
||||
|
||||
|
||||
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 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 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 and SpatiaLite 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 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(),
|
||||
}
|
||||
|
||||
return self._spatial_attribute('snap_to_grid', s, **kwargs)
|
||||
|
||||
def svg(self, relative=False, precision=8, **kwargs):
|
||||
"""
|
||||
Returns SVG representation of the geographic field in a `svg`
|
||||
attribute on each element of this GeoQuerySet.
|
||||
|
||||
Keyword Arguments:
|
||||
`relative` => If set to True, this will evaluate the path in
|
||||
terms of relative moves (rather than absolute).
|
||||
|
||||
`precision` => May be used to set the maximum number of decimal
|
||||
digits used in output (defaults to 8).
|
||||
"""
|
||||
relative = int(bool(relative))
|
||||
if not isinstance(precision, six.integer_types):
|
||||
raise TypeError('SVG precision keyword argument must be an integer.')
|
||||
s = {
|
||||
'desc': 'SVG',
|
||||
'procedure_fmt': '%(geo_col)s,%(rel)s,%(precision)s',
|
||||
'procedure_args': {
|
||||
'rel': relative,
|
||||
'precision': precision,
|
||||
}
|
||||
}
|
||||
return self._spatial_attribute('svg', s, **kwargs)
|
||||
|
||||
def sym_difference(self, geom, **kwargs):
|
||||
"""
|
||||
Returns the symmetric difference of the geographic field in a
|
||||
`sym_difference` attribute on each element of this GeoQuerySet.
|
||||
"""
|
||||
return self._geomset_attribute('sym_difference', geom, **kwargs)
|
||||
|
||||
def translate(self, x, y, z=0.0, **kwargs):
|
||||
"""
|
||||
Translates the geometry to a new location using the given numeric
|
||||
parameters as offsets.
|
||||
"""
|
||||
if connections[self.db].ops.spatialite:
|
||||
if z != 0.0:
|
||||
raise NotImplementedError('SpatiaLite does not support 3D translation.')
|
||||
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('translate', s, **kwargs)
|
||||
|
||||
def transform(self, srid=4326, **kwargs):
|
||||
"""
|
||||
Transforms the given geometry field to the given SRID. If no SRID is
|
||||
provided, the transformation will default to using 4326 (WGS84).
|
||||
"""
|
||||
if not isinstance(srid, six.integer_types):
|
||||
raise TypeError('An integer SRID must be provided.')
|
||||
field_name = kwargs.get('field_name')
|
||||
self._spatial_setup('transform', field_name=field_name)
|
||||
self.query.add_context('transformed_srid', srid)
|
||||
return self._clone()
|
||||
|
||||
def union(self, geom, **kwargs):
|
||||
"""
|
||||
Returns the union of the geographic field with the given
|
||||
Geometry in a `union` attribute on each element of this GeoQuerySet.
|
||||
"""
|
||||
return self._geomset_attribute('union', geom, **kwargs)
|
||||
|
||||
# ### Private API -- Abstracted DRY routines. ###
|
||||
def _spatial_setup(self, att, desc=None, field_name=None, geo_field_type=None):
|
||||
"""
|
||||
Performs set up for executing the spatial function.
|
||||
"""
|
||||
# Does the spatial backend support this?
|
||||
connection = connections[self.db]
|
||||
func = getattr(connection.ops, att, False)
|
||||
if desc is None:
|
||||
desc = att
|
||||
if not func:
|
||||
raise NotImplementedError('%s stored procedure not available on '
|
||||
'the %s backend.' %
|
||||
(desc, connection.ops.name))
|
||||
|
||||
# Initializing the procedure arguments.
|
||||
procedure_args = {'function': func}
|
||||
|
||||
# Is there a geographic field in the model to perform this
|
||||
# operation on?
|
||||
geo_field = self._geo_field(field_name)
|
||||
if not geo_field:
|
||||
raise TypeError('%s output only available on GeometryFields.' % func)
|
||||
|
||||
# If the `geo_field_type` keyword was used, then enforce that
|
||||
# type limitation.
|
||||
if geo_field_type is not None and not isinstance(geo_field, geo_field_type):
|
||||
raise TypeError('"%s" stored procedures may only be called on %ss.' % (func, geo_field_type.__name__))
|
||||
|
||||
# Setting the procedure args.
|
||||
procedure_args['geo_col'] = self._geocol_select(geo_field, field_name)
|
||||
|
||||
return procedure_args, geo_field
|
||||
|
||||
def _spatial_attribute(self, att, settings, field_name=None, model_att=None):
|
||||
"""
|
||||
DRY routine for calling a spatial stored procedure on a geometry column
|
||||
and attaching its output as an attribute of the model.
|
||||
|
||||
Arguments:
|
||||
att:
|
||||
The name of the spatial attribute that holds the spatial
|
||||
SQL function to call.
|
||||
|
||||
settings:
|
||||
Dictionary of internal settings to customize for the spatial procedure.
|
||||
|
||||
Public Keyword Arguments:
|
||||
|
||||
field_name:
|
||||
The name of the geographic field to call the spatial
|
||||
function on. May also be a lookup to a geometry field
|
||||
as part of a foreign key relation.
|
||||
|
||||
model_att:
|
||||
The name of the model attribute to attach the output of
|
||||
the spatial function to.
|
||||
"""
|
||||
warnings.warn(
|
||||
"The %s GeoQuerySet method is deprecated. See GeoDjango Functions "
|
||||
"documentation to find the expression-based replacement." % att,
|
||||
RemovedInDjango20Warning, stacklevel=2
|
||||
)
|
||||
# Default settings.
|
||||
settings.setdefault('desc', None)
|
||||
settings.setdefault('geom_args', ())
|
||||
settings.setdefault('geom_field', None)
|
||||
settings.setdefault('procedure_args', {})
|
||||
settings.setdefault('procedure_fmt', '%(geo_col)s')
|
||||
settings.setdefault('select_params', [])
|
||||
|
||||
connection = connections[self.db]
|
||||
|
||||
# Performing setup for the spatial column, unless told not to.
|
||||
if settings.get('setup', True):
|
||||
default_args, geo_field = self._spatial_setup(
|
||||
att, desc=settings['desc'], field_name=field_name,
|
||||
geo_field_type=settings.get('geo_field_type'))
|
||||
for k, v in six.iteritems(default_args):
|
||||
settings['procedure_args'].setdefault(k, v)
|
||||
else:
|
||||
geo_field = settings['geo_field']
|
||||
|
||||
# The attribute to attach to the model.
|
||||
if not isinstance(model_att, six.string_types):
|
||||
model_att = att
|
||||
|
||||
# Special handling for any argument that is a geometry.
|
||||
for name in settings['geom_args']:
|
||||
# Using the field's get_placeholder() routine to get any needed
|
||||
# transformation SQL.
|
||||
geom = geo_field.get_prep_value(settings['procedure_args'][name])
|
||||
params = geo_field._get_db_prep_lookup('contains', geom, connection=connection)
|
||||
geom_placeholder = geo_field.get_placeholder(geom, None, connection)
|
||||
|
||||
# Replacing the procedure format with that of any needed
|
||||
# transformation SQL.
|
||||
old_fmt = '%%(%s)s' % name
|
||||
new_fmt = geom_placeholder % '%%s'
|
||||
settings['procedure_fmt'] = settings['procedure_fmt'].replace(old_fmt, new_fmt)
|
||||
settings['select_params'].extend(params)
|
||||
|
||||
# Getting the format for the stored procedure.
|
||||
fmt = '%%(function)s(%s)' % settings['procedure_fmt']
|
||||
|
||||
# If the result of this function needs to be converted.
|
||||
if settings.get('select_field'):
|
||||
select_field = settings['select_field']
|
||||
if connection.ops.oracle:
|
||||
select_field.empty_strings_allowed = False
|
||||
else:
|
||||
select_field = Field()
|
||||
|
||||
# Finally, setting the extra selection attribute with
|
||||
# the format string expanded with the stored procedure
|
||||
# arguments.
|
||||
self.query.add_annotation(
|
||||
RawSQL(fmt % settings['procedure_args'], settings['select_params'], select_field),
|
||||
model_att)
|
||||
return self
|
||||
|
||||
def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs):
|
||||
"""
|
||||
DRY routine for GeoQuerySet distance attribute routines.
|
||||
"""
|
||||
# Setting up the distance procedure arguments.
|
||||
procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name'))
|
||||
|
||||
# If geodetic defaulting distance attribute to meters (Oracle and
|
||||
# PostGIS spherical distances return meters). Otherwise, use the
|
||||
# units of the geometry field.
|
||||
connection = connections[self.db]
|
||||
geodetic = geo_field.geodetic(connection)
|
||||
geography = geo_field.geography
|
||||
|
||||
if geodetic:
|
||||
dist_att = 'm'
|
||||
else:
|
||||
dist_att = Distance.unit_attname(geo_field.units_name(connection))
|
||||
|
||||
# Shortcut booleans for what distance function we're using and
|
||||
# whether the geometry field is 3D.
|
||||
distance = func == 'distance'
|
||||
length = func == 'length'
|
||||
perimeter = func == 'perimeter'
|
||||
if not (distance or length or perimeter):
|
||||
raise ValueError('Unknown distance function: %s' % func)
|
||||
geom_3d = geo_field.dim == 3
|
||||
|
||||
# The field's _get_db_prep_lookup() is used to get any
|
||||
# extra distance parameters. Here we set up the
|
||||
# parameters that will be passed in to field's function.
|
||||
lookup_params = [geom or 'POINT (0 0)', 0]
|
||||
|
||||
# Getting the spatial backend operations.
|
||||
backend = connection.ops
|
||||
|
||||
# If the spheroid calculation is desired, either by the `spheroid`
|
||||
# keyword or when calculating the length of geodetic field, make
|
||||
# sure the 'spheroid' distance setting string is passed in so we
|
||||
# get the correct spatial stored procedure.
|
||||
if spheroid or (backend.postgis and geodetic and
|
||||
(not geography) and length):
|
||||
lookup_params.append('spheroid')
|
||||
lookup_params = geo_field.get_prep_value(lookup_params)
|
||||
params = geo_field._get_db_prep_lookup('distance_lte', lookup_params, connection=connection)
|
||||
|
||||
# The `geom_args` flag is set to true if a geometry parameter was
|
||||
# passed in.
|
||||
geom_args = bool(geom)
|
||||
|
||||
if backend.oracle:
|
||||
if distance:
|
||||
procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s'
|
||||
elif length or perimeter:
|
||||
procedure_fmt = '%(geo_col)s,%(tolerance)s'
|
||||
procedure_args['tolerance'] = tolerance
|
||||
else:
|
||||
# Getting whether this field is in units of degrees since the field may have
|
||||
# been transformed via the `transform` GeoQuerySet method.
|
||||
srid = self.query.get_context('transformed_srid')
|
||||
if srid:
|
||||
u, unit_name, s = get_srid_info(srid, connection)
|
||||
geodetic = unit_name.lower() in geo_field.geodetic_units
|
||||
|
||||
if geodetic and (not connection.features.supports_distance_geodetic or connection.ops.spatialite):
|
||||
raise ValueError(
|
||||
'This database does not support linear distance '
|
||||
'calculations on geodetic coordinate systems.'
|
||||
)
|
||||
|
||||
if distance:
|
||||
if srid:
|
||||
# Setting the `geom_args` flag to false because we want to handle
|
||||
# transformation SQL here, rather than the way done by default
|
||||
# (which will transform to the original SRID of the field rather
|
||||
# than to what was transformed to).
|
||||
geom_args = False
|
||||
procedure_fmt = '%s(%%(geo_col)s, %s)' % (backend.transform, srid)
|
||||
if geom.srid is None or geom.srid == srid:
|
||||
# If the geom parameter srid is None, it is assumed the coordinates
|
||||
# are in the transformed units. A placeholder is used for the
|
||||
# geometry parameter. `GeomFromText` constructor is also needed
|
||||
# to wrap geom placeholder for SpatiaLite.
|
||||
if backend.spatialite:
|
||||
procedure_fmt += ', %s(%%%%s, %s)' % (backend.from_text, srid)
|
||||
else:
|
||||
procedure_fmt += ', %%s'
|
||||
else:
|
||||
# We need to transform the geom to the srid specified in `transform()`,
|
||||
# so wrapping the geometry placeholder in transformation SQL.
|
||||
# SpatiaLite also needs geometry placeholder wrapped in `GeomFromText`
|
||||
# constructor.
|
||||
if backend.spatialite:
|
||||
procedure_fmt += (', %s(%s(%%%%s, %s), %s)' % (
|
||||
backend.transform, backend.from_text,
|
||||
geom.srid, srid))
|
||||
else:
|
||||
procedure_fmt += ', %s(%%%%s, %s)' % (backend.transform, srid)
|
||||
else:
|
||||
# `transform()` was not used on this GeoQuerySet.
|
||||
procedure_fmt = '%(geo_col)s,%(geom)s'
|
||||
|
||||
if not geography and geodetic:
|
||||
# Spherical distance calculation is needed (because the geographic
|
||||
# field is geodetic). However, the PostGIS ST_distance_sphere/spheroid()
|
||||
# procedures may only do queries from point columns to point geometries
|
||||
# some error checking is required.
|
||||
if not backend.geography:
|
||||
if not isinstance(geo_field, PointField):
|
||||
raise ValueError('Spherical distance calculation only supported on PointFields.')
|
||||
if not str(Geometry(six.memoryview(params[0].ewkb)).geom_type) == 'Point':
|
||||
raise ValueError(
|
||||
'Spherical distance calculation only supported with '
|
||||
'Point Geometry parameters'
|
||||
)
|
||||
# The `function` procedure argument needs to be set differently for
|
||||
# geodetic distance calculations.
|
||||
if spheroid:
|
||||
# Call to distance_spheroid() requires spheroid param as well.
|
||||
procedure_fmt += ",'%(spheroid)s'"
|
||||
procedure_args.update({'function': backend.distance_spheroid, 'spheroid': params[1]})
|
||||
else:
|
||||
procedure_args.update({'function': backend.distance_sphere})
|
||||
elif length or perimeter:
|
||||
procedure_fmt = '%(geo_col)s'
|
||||
if not geography and geodetic and length:
|
||||
# There's no `length_sphere`, and `length_spheroid` also
|
||||
# works on 3D geometries.
|
||||
procedure_fmt += ",'%(spheroid)s'"
|
||||
procedure_args.update({'function': backend.length_spheroid, 'spheroid': params[1]})
|
||||
elif geom_3d and connection.features.supports_3d_functions:
|
||||
# Use 3D variants of perimeter and length routines on supported backends.
|
||||
if perimeter:
|
||||
procedure_args.update({'function': backend.perimeter3d})
|
||||
elif length:
|
||||
procedure_args.update({'function': backend.length3d})
|
||||
|
||||
# Setting up the settings for `_spatial_attribute`.
|
||||
s = {'select_field': DistanceField(dist_att),
|
||||
'setup': False,
|
||||
'geo_field': geo_field,
|
||||
'procedure_args': procedure_args,
|
||||
'procedure_fmt': procedure_fmt,
|
||||
}
|
||||
if geom_args:
|
||||
s['geom_args'] = ('geom',)
|
||||
s['procedure_args']['geom'] = geom
|
||||
elif geom:
|
||||
# The geometry is passed in as a parameter because we handled
|
||||
# transformation conditions in this routine.
|
||||
s['select_params'] = [backend.Adapter(geom)]
|
||||
return self._spatial_attribute(func, s, **kwargs)
|
||||
|
||||
def _geom_attribute(self, func, tolerance=0.05, **kwargs):
|
||||
"""
|
||||
DRY routine for setting up a GeoQuerySet method that attaches a
|
||||
Geometry attribute (e.g., `centroid`, `point_on_surface`).
|
||||
"""
|
||||
s = {'select_field': GeomField()}
|
||||
if connections[self.db].ops.oracle:
|
||||
s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
|
||||
s['procedure_args'] = {'tolerance': tolerance}
|
||||
return self._spatial_attribute(func, s, **kwargs)
|
||||
|
||||
def _geomset_attribute(self, func, geom, tolerance=0.05, **kwargs):
|
||||
"""
|
||||
DRY routine for setting up a GeoQuerySet method that attaches a
|
||||
Geometry attribute and takes a Geoemtry parameter. This is used
|
||||
for geometry set-like operations (e.g., intersection, difference,
|
||||
union, sym_difference).
|
||||
"""
|
||||
s = {
|
||||
'geom_args': ('geom',),
|
||||
'select_field': GeomField(),
|
||||
'procedure_fmt': '%(geo_col)s,%(geom)s',
|
||||
'procedure_args': {'geom': geom},
|
||||
}
|
||||
if connections[self.db].ops.oracle:
|
||||
s['procedure_fmt'] += ',%(tolerance)s'
|
||||
s['procedure_args']['tolerance'] = tolerance
|
||||
return self._spatial_attribute(func, s, **kwargs)
|
||||
|
||||
def _geocol_select(self, geo_field, field_name):
|
||||
"""
|
||||
Helper routine for constructing the SQL to select the geographic
|
||||
column. Takes into account if the geographic field is in a
|
||||
ForeignKey relation to the current model.
|
||||
"""
|
||||
compiler = self.query.get_compiler(self.db)
|
||||
opts = self.model._meta
|
||||
if geo_field not in opts.fields:
|
||||
# Is this operation going to be on a related geographic field?
|
||||
# If so, it'll have to be added to the select related information
|
||||
# (e.g., if 'location__point' was given as the field name, then
|
||||
# chop the non-relational field and add select_related('location')).
|
||||
# Note: the operation really is defined as "must add select related!"
|
||||
self.query.add_select_related([field_name.rsplit(LOOKUP_SEP, 1)[0]])
|
||||
# Call pre_sql_setup() so that compiler.select gets populated.
|
||||
compiler.pre_sql_setup()
|
||||
for col, _, _ in compiler.select:
|
||||
if col.output_field == geo_field:
|
||||
return col.as_sql(compiler, compiler.connection)[0]
|
||||
raise ValueError("%r not in compiler's related_select_cols" % geo_field)
|
||||
elif geo_field not in opts.local_fields:
|
||||
# This geographic field is inherited from another model, so we have to
|
||||
# use the db table for the _parent_ model instead.
|
||||
parent_model = geo_field.model._meta.concrete_model
|
||||
return self._field_column(compiler, geo_field, parent_model._meta.db_table)
|
||||
else:
|
||||
return self._field_column(compiler, geo_field)
|
||||
|
||||
# Private API utilities, subject to change.
|
||||
def _geo_field(self, field_name=None):
|
||||
"""
|
||||
Returns the first Geometry field encountered or the one specified via
|
||||
the `field_name` keyword. The `field_name` may be a string specifying
|
||||
the geometry field on this GeoQuerySet's model, or a lookup string
|
||||
to a geometry field via a ForeignKey relation.
|
||||
"""
|
||||
if field_name is None:
|
||||
# Incrementing until the first geographic field is found.
|
||||
for field in self.model._meta.fields:
|
||||
if isinstance(field, GeometryField):
|
||||
return field
|
||||
return False
|
||||
else:
|
||||
# Otherwise, check by the given field name -- which may be
|
||||
# a lookup to a _related_ geographic field.
|
||||
return GISLookup._check_geo_field(self.model._meta, field_name)
|
||||
|
||||
def _field_column(self, compiler, field, table_alias=None, column=None):
|
||||
"""
|
||||
Helper function that returns the database column for the given field.
|
||||
The table and column are returned (quoted) in the proper format, e.g.,
|
||||
`"geoapp_city"."point"`. If `table_alias` is not specified, the
|
||||
database table associated with the model of this `GeoQuerySet` will be
|
||||
used. If `column` is specified, it will be used instead of the value
|
||||
in `field.column`.
|
||||
"""
|
||||
if table_alias is None:
|
||||
table_alias = compiler.query.get_meta().db_table
|
||||
return "%s.%s" % (compiler.quote_name_unless_alias(table_alias),
|
||||
compiler.connection.ops.quote_name(column or field.column))
|
|
@ -1,11 +1,9 @@
|
|||
=========================
|
||||
GeoQuerySet API Reference
|
||||
=========================
|
||||
==========================
|
||||
GIS QuerySet API Reference
|
||||
==========================
|
||||
|
||||
.. currentmodule:: django.contrib.gis.db.models
|
||||
|
||||
.. class:: GeoQuerySet(model=None)
|
||||
|
||||
.. _spatial-lookups:
|
||||
|
||||
Spatial Lookups
|
||||
|
@ -720,593 +718,6 @@ SpatiaLite ``PtDistWithin(poly, geom, 5)``
|
|||
|
||||
SpatiaLite support was added.
|
||||
|
||||
.. _geoqueryset-methods:
|
||||
|
||||
``GeoQuerySet`` Methods
|
||||
=======================
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Using ``GeoQuerySet`` methods is now deprecated in favor of the new
|
||||
:doc:`functions`. Albeit a little more verbose, they are much more powerful
|
||||
in how it is possible to combine them to build more complex queries.
|
||||
|
||||
``GeoQuerySet`` methods specify that a spatial operation be performed
|
||||
on each spatial operation on each geographic
|
||||
field in the queryset and store its output in a new attribute on the model
|
||||
(which is generally the name of the ``GeoQuerySet`` method).
|
||||
|
||||
There are also aggregate ``GeoQuerySet`` methods which return a single value
|
||||
instead of a queryset. This section will describe the API and availability
|
||||
of every ``GeoQuerySet`` method available in GeoDjango.
|
||||
|
||||
.. note::
|
||||
|
||||
What methods are available depend on your spatial backend. See
|
||||
the :ref:`compatibility table <database-functions-compatibility>`
|
||||
for more details.
|
||||
|
||||
With a few exceptions, the following keyword arguments may be used with all
|
||||
``GeoQuerySet`` methods:
|
||||
|
||||
===================== =====================================================
|
||||
Keyword Argument Description
|
||||
===================== =====================================================
|
||||
``field_name`` By default, ``GeoQuerySet`` methods use the first
|
||||
geographic field encountered in the model. This
|
||||
keyword should be used to specify another
|
||||
geographic field (e.g., ``field_name='point2'``)
|
||||
when there are multiple geographic fields in a model.
|
||||
|
||||
On PostGIS, the ``field_name`` keyword may also be
|
||||
used on geometry fields in models that are related
|
||||
via a ``ForeignKey`` relation (e.g.,
|
||||
``field_name='related__point'``).
|
||||
|
||||
``model_att`` By default, ``GeoQuerySet`` methods typically attach
|
||||
their output in an attribute with the same name as
|
||||
the ``GeoQuerySet`` method. Setting this keyword
|
||||
with the desired attribute name will override this
|
||||
default behavior. For example,
|
||||
``qs = Zipcode.objects.centroid(model_att='c')`` will
|
||||
attach the centroid of the ``Zipcode`` geometry field
|
||||
in a ``c`` attribute on every model rather than in a
|
||||
``centroid`` attribute.
|
||||
|
||||
This keyword is required if
|
||||
a method name clashes with an existing
|
||||
``GeoQuerySet`` method -- if you wanted to use the
|
||||
``area()`` method on model with a ``PolygonField``
|
||||
named ``area``, for example.
|
||||
===================== =====================================================
|
||||
|
||||
Measurement
|
||||
-----------
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
``area``
|
||||
~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.area(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Area` function
|
||||
instead.
|
||||
|
||||
Returns the area of the geographic field in an ``area`` attribute on
|
||||
each element of this GeoQuerySet.
|
||||
|
||||
``distance``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.distance(geom, **kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Distance` function
|
||||
instead.
|
||||
|
||||
This method takes a geometry as a parameter, and attaches a ``distance``
|
||||
attribute to every model in the returned queryset that contains the
|
||||
distance (as a :class:`~django.contrib.gis.measure.Distance` object) to the given geometry.
|
||||
|
||||
In the following example (taken from the `GeoDjango distance tests`__),
|
||||
the distance from the `Tasmanian`__ city of Hobart to every other
|
||||
:class:`PointField` in the ``AustraliaCity`` queryset is calculated::
|
||||
|
||||
>>> pnt = AustraliaCity.objects.get(name='Hobart').point
|
||||
>>> for city in AustraliaCity.objects.distance(pnt): print(city.name, city.distance)
|
||||
Wollongong 990071.220408 m
|
||||
Shellharbour 972804.613941 m
|
||||
Thirroul 1002334.36351 m
|
||||
Mittagong 975691.632637 m
|
||||
Batemans Bay 834342.185561 m
|
||||
Canberra 598140.268959 m
|
||||
Melbourne 575337.765042 m
|
||||
Sydney 1056978.87363 m
|
||||
Hobart 0.0 m
|
||||
Adelaide 1162031.83522 m
|
||||
Hillsdale 1049200.46122 m
|
||||
|
||||
.. note::
|
||||
|
||||
Because the ``distance`` attribute is a
|
||||
:class:`~django.contrib.gis.measure.Distance` object, you can easily express
|
||||
the value in the units of your choice. For example, ``city.distance.mi`` is
|
||||
the distance value in miles and ``city.distance.km`` is the distance value
|
||||
in kilometers. See :doc:`measure` for usage details and the list of
|
||||
:ref:`supported_units`.
|
||||
|
||||
__ https://github.com/django/django/blob/master/tests/gis_tests/distapp/models.py
|
||||
__ https://en.wikipedia.org/wiki/Tasmania
|
||||
|
||||
``length``
|
||||
~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.length(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Length` function
|
||||
instead.
|
||||
|
||||
Returns the length of the geometry field in a ``length`` attribute
|
||||
(a :class:`~django.contrib.gis.measure.Distance` object) on each model in
|
||||
the queryset.
|
||||
|
||||
``perimeter``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.perimeter(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Perimeter` function
|
||||
instead.
|
||||
|
||||
Returns the perimeter of the geometry field in a ``perimeter`` attribute
|
||||
(a :class:`~django.contrib.gis.measure.Distance` object) on each model in
|
||||
the queryset.
|
||||
|
||||
Geometry Relationships
|
||||
----------------------
|
||||
|
||||
The following methods take no arguments, and attach geometry objects
|
||||
each element of the :class:`GeoQuerySet` that is the result of relationship
|
||||
function evaluated on the geometry field.
|
||||
|
||||
``centroid``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.centroid(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Centroid` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
Returns the ``centroid`` value for the geographic field in a ``centroid``
|
||||
attribute on each element of the ``GeoQuerySet``.
|
||||
|
||||
``envelope``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.envelope(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Envelope` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, SpatiaLite
|
||||
|
||||
Returns a geometry representing the bounding box of the geometry field in
|
||||
an ``envelope`` attribute on each element of the ``GeoQuerySet``.
|
||||
|
||||
``point_on_surface``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.point_on_surface(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.PointOnSurface`
|
||||
function instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
Returns a Point geometry guaranteed to lie on the surface of the
|
||||
geometry field in a ``point_on_surface`` attribute on each element
|
||||
of the queryset; otherwise sets with None.
|
||||
|
||||
Geometry Editors
|
||||
----------------
|
||||
|
||||
``force_rhr``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.force_rhr(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.ForceRHR` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS
|
||||
|
||||
Returns a modified version of the polygon/multipolygon in which all
|
||||
of the vertices follow the Right-Hand-Rule, and attaches as a
|
||||
``force_rhr`` attribute on each element of the queryset.
|
||||
|
||||
``reverse_geom``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.reverse_geom(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Reverse` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle
|
||||
|
||||
Reverse the coordinate order of the geometry field, and attaches as a
|
||||
``reverse`` attribute on each element of the queryset.
|
||||
|
||||
``scale``
|
||||
~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.scale(x, y, z=0.0, **kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Scale` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, SpatiaLite
|
||||
|
||||
``snap_to_grid``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.snap_to_grid(*args, **kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.SnapToGrid` function
|
||||
instead.
|
||||
|
||||
Snap all points of the input geometry to the grid. How the
|
||||
geometry is snapped to the grid depends on how many numeric
|
||||
(either float, integer, or long) arguments are given.
|
||||
|
||||
=================== =====================================================
|
||||
Number of Arguments Description
|
||||
=================== =====================================================
|
||||
1 A single size to snap bot the X and Y grids to.
|
||||
2 X and Y sizes to snap the grid to.
|
||||
4 X, Y sizes and the corresponding X, Y origins.
|
||||
=================== =====================================================
|
||||
|
||||
``transform``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.transform(srid=4326, **kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Transform` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
The ``transform`` method transforms the geometry field of a model to the spatial
|
||||
reference system specified by the ``srid`` parameter. If no ``srid`` is given,
|
||||
then 4326 (WGS84) is used by default.
|
||||
|
||||
.. note::
|
||||
|
||||
Unlike other ``GeoQuerySet`` methods, ``transform`` stores its output
|
||||
"in-place". In other words, no new attribute for the transformed
|
||||
geometry is placed on the models.
|
||||
|
||||
.. note::
|
||||
|
||||
What spatial reference system an integer SRID corresponds to may depend on
|
||||
the spatial database used. In other words, the SRID numbers used for Oracle
|
||||
are not necessarily the same as those used by PostGIS.
|
||||
|
||||
Example::
|
||||
|
||||
>>> qs = Zipcode.objects.all().transform() # Transforms to WGS84
|
||||
>>> qs = Zipcode.objects.all().transform(32140) # Transforming to "NAD83 / Texas South Central"
|
||||
>>> print(qs[0].poly.srid)
|
||||
32140
|
||||
>>> print(qs[0].poly)
|
||||
POLYGON ((234055.1698884720099159 4937796.9232223574072123 ...
|
||||
|
||||
``translate``
|
||||
~~~~~~~~~~~~~
|
||||
.. method:: GeoQuerySet.translate(x, y, z=0.0, **kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Translate` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, SpatiaLite
|
||||
|
||||
Translates the geometry field to a new location using the given numeric
|
||||
parameters as offsets.
|
||||
|
||||
Geometry Operations
|
||||
-------------------
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
The following methods all take a geometry as a parameter and attach a geometry
|
||||
to each element of the ``GeoQuerySet`` that is the result of the operation.
|
||||
|
||||
``difference``
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.difference(geom)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Difference` function
|
||||
instead.
|
||||
|
||||
Returns the spatial difference of the geographic field with the given
|
||||
geometry in a ``difference`` attribute on each element of the
|
||||
``GeoQuerySet``.
|
||||
|
||||
|
||||
``intersection``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.intersection(geom)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Intersection`
|
||||
function instead.
|
||||
|
||||
Returns the spatial intersection of the geographic field with the
|
||||
given geometry in an ``intersection`` attribute on each element of the
|
||||
``GeoQuerySet``.
|
||||
|
||||
``sym_difference``
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.sym_difference(geom)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.SymDifference`
|
||||
function instead.
|
||||
|
||||
Returns the symmetric difference of the geographic field with the
|
||||
given geometry in a ``sym_difference`` attribute on each element of the
|
||||
``GeoQuerySet``.
|
||||
|
||||
``union``
|
||||
~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.union(geom)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.Union` function
|
||||
instead.
|
||||
|
||||
Returns the union of the geographic field with the given
|
||||
geometry in an ``union`` attribute on each element of the
|
||||
``GeoQuerySet``.
|
||||
|
||||
Geometry Output
|
||||
---------------
|
||||
|
||||
The following ``GeoQuerySet`` methods will return an attribute that has the value
|
||||
of the geometry field in each model converted to the requested output format.
|
||||
|
||||
``geohash``
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.geohash(precision=20, **kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.GeoHash` function
|
||||
instead.
|
||||
|
||||
Attaches a ``geohash`` attribute to every model the queryset
|
||||
containing the `GeoHash`__ representation of the geometry.
|
||||
|
||||
__ http://geohash.org/
|
||||
|
||||
``geojson``
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.geojson(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.AsGeoJSON` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, SpatiaLite
|
||||
|
||||
Attaches a ``geojson`` attribute to every model in the queryset that contains the
|
||||
`GeoJSON`__ representation of the geometry.
|
||||
|
||||
===================== =====================================================
|
||||
Keyword Argument Description
|
||||
===================== =====================================================
|
||||
``precision`` It may be used to specify the number of significant
|
||||
digits for the coordinates in the GeoJSON
|
||||
representation -- the default value is 8.
|
||||
|
||||
``crs`` Set this to ``True`` if you want the coordinate
|
||||
reference system to be included in the returned
|
||||
GeoJSON.
|
||||
|
||||
``bbox`` Set this to ``True`` if you want the bounding box
|
||||
to be included in the returned GeoJSON.
|
||||
===================== =====================================================
|
||||
|
||||
__ http://geojson.org/
|
||||
|
||||
``gml``
|
||||
~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.gml(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.AsGML` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
Attaches a ``gml`` attribute to every model in the queryset that contains the
|
||||
`Geographic Markup Language (GML)`__ representation of the geometry.
|
||||
|
||||
Example::
|
||||
|
||||
>>> qs = Zipcode.objects.all().gml()
|
||||
>>> print(qs[0].gml)
|
||||
<gml:Polygon srsName="EPSG:4326"><gml:OuterBoundaryIs>-147.78711,70.245363 ... -147.78711,70.245363</gml:OuterBoundaryIs></gml:Polygon>
|
||||
|
||||
===================== =====================================================
|
||||
Keyword Argument Description
|
||||
===================== =====================================================
|
||||
``precision`` This keyword is for PostGIS only. It may be used
|
||||
to specify the number of significant digits for the
|
||||
coordinates in the GML representation -- the default
|
||||
value is 8.
|
||||
|
||||
``version`` This keyword is for PostGIS only. It may be used to
|
||||
specify the GML version used, and may only be values
|
||||
of 2 or 3. The default value is 2.
|
||||
===================== =====================================================
|
||||
|
||||
__ https://en.wikipedia.org/wiki/Geography_Markup_Language
|
||||
|
||||
``kml``
|
||||
~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.kml(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.AsKML` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, SpatiaLite
|
||||
|
||||
Attaches a ``kml`` attribute to every model in the queryset that contains the
|
||||
`Keyhole Markup Language (KML)`__ representation of the geometry fields. It
|
||||
should be noted that the contents of the KML are transformed to WGS84 if
|
||||
necessary.
|
||||
|
||||
Example::
|
||||
|
||||
>>> qs = Zipcode.objects.all().kml()
|
||||
>>> print(qs[0].kml)
|
||||
<Polygon><outerBoundaryIs><LinearRing><coordinates>-103.04135,36.217596,0 ... -103.04135,36.217596,0</coordinates></LinearRing></outerBoundaryIs></Polygon>
|
||||
|
||||
===================== =====================================================
|
||||
Keyword Argument Description
|
||||
===================== =====================================================
|
||||
``precision`` This keyword may be used to specify the number of
|
||||
significant digits for the coordinates in the KML
|
||||
representation -- the default value is 8.
|
||||
===================== =====================================================
|
||||
|
||||
__ https://developers.google.com/kml/documentation/
|
||||
|
||||
``svg``
|
||||
~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.svg(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.AsSVG` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, SpatiaLite
|
||||
|
||||
Attaches a ``svg`` attribute to every model in the queryset that contains
|
||||
the `Scalable Vector Graphics (SVG)`__ path data of the geometry fields.
|
||||
|
||||
===================== =====================================================
|
||||
Keyword Argument Description
|
||||
===================== =====================================================
|
||||
``relative`` If set to ``True``, the path data will be implemented
|
||||
in terms of relative moves. Defaults to ``False``,
|
||||
meaning that absolute moves are used instead.
|
||||
|
||||
``precision`` This keyword may be used to specify the number of
|
||||
significant digits for the coordinates in the SVG
|
||||
representation -- the default value is 8.
|
||||
===================== =====================================================
|
||||
|
||||
__ http://www.w3.org/Graphics/SVG/
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
``mem_size``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.mem_size(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.MemSize` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS
|
||||
|
||||
Returns the memory size (number of bytes) that the geometry field takes
|
||||
in a ``mem_size`` attribute on each element of the ``GeoQuerySet``.
|
||||
|
||||
``num_geom``
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.num_geom(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.NumGeometries`
|
||||
function instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
Returns the number of geometries in a ``num_geom`` attribute on
|
||||
each element of the ``GeoQuerySet`` if the geometry field is a
|
||||
collection (e.g., a ``GEOMETRYCOLLECTION`` or ``MULTI*`` field);
|
||||
otherwise sets with ``None``.
|
||||
|
||||
``num_points``
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: GeoQuerySet.num_points(**kwargs)
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
Use the :class:`~django.contrib.gis.db.models.functions.NumPoints` function
|
||||
instead.
|
||||
|
||||
*Availability*: PostGIS, Oracle, SpatiaLite
|
||||
|
||||
Returns the number of points in the first linestring in the
|
||||
geometry field in a ``num_points`` attribute on each element of
|
||||
the ``GeoQuerySet``; otherwise sets with ``None``.
|
||||
|
||||
Aggregate Functions
|
||||
-------------------
|
||||
|
||||
|
|
|
@ -244,22 +244,6 @@ For more information, the PostGIS documentation contains a helpful section on
|
|||
determining `when to use geography data type over geometry data type
|
||||
<http://postgis.net/docs/using_postgis_dbmanagement.html#PostGIS_GeographyVSGeometry>`_.
|
||||
|
||||
``GeoManager``
|
||||
==============
|
||||
|
||||
.. currentmodule:: django.contrib.gis.db.models
|
||||
.. class:: GeoManager
|
||||
|
||||
The ``GeoManager`` is required in order to use the legacy
|
||||
:ref:`geoqueryset-methods`.
|
||||
|
||||
.. deprecated:: 1.9
|
||||
|
||||
All ``GeoQuerySet`` methods have been deprecated and replaced by
|
||||
:doc:`equivalent database functions </ref/contrib/gis/functions>`. As soon
|
||||
as the legacy methods have been replaced in your code, you should be able
|
||||
to remove the special ``GeoManager`` from your GIS-enabled classes.
|
||||
|
||||
.. rubric:: Footnotes
|
||||
.. [#fnogc] OpenGIS Consortium, Inc., `Simple Feature Specification For SQL <http://www.opengeospatial.org/standards/sfs>`_.
|
||||
.. [#fnogcsrid] *See id.* at Ch. 2.3.8, p. 39 (Geometry Values and Spatial Reference Systems).
|
||||
|
|
|
@ -149,11 +149,10 @@ Here's the formal declaration of a ``QuerySet``:
|
|||
.. note::
|
||||
|
||||
The ``query`` parameter to :class:`QuerySet` exists so that specialized
|
||||
query subclasses such as
|
||||
:class:`~django.contrib.gis.db.models.GeoQuerySet` can reconstruct
|
||||
internal query state. The value of the parameter is an opaque
|
||||
representation of that query state and is not part of a public API.
|
||||
To put it simply: if you need to ask, you don't need to use it.
|
||||
query subclasses can reconstruct internal query state. The value of the
|
||||
parameter is an opaque representation of that query state and is not
|
||||
part of a public API. To put it simply: if you need to ask, you don't
|
||||
need to use it.
|
||||
|
||||
.. currentmodule:: django.db.models.query.QuerySet
|
||||
|
||||
|
|
|
@ -359,12 +359,8 @@ keyword to 3 in your :class:`~django.contrib.gis.db.models.GeometryField`.
|
|||
The :class:`~django.contrib.gis.db.models.Extent3D` aggregate
|
||||
and ``extent3d()`` ``GeoQuerySet`` method were added as a part of this feature.
|
||||
|
||||
The following :class:`~django.contrib.gis.db.models.GeoQuerySet`
|
||||
methods are new in 1.2:
|
||||
|
||||
* :meth:`~django.contrib.gis.db.models.GeoQuerySet.force_rhr`
|
||||
* :meth:`~django.contrib.gis.db.models.GeoQuerySet.reverse_geom`
|
||||
* :meth:`~django.contrib.gis.db.models.GeoQuerySet.geohash`
|
||||
The ``force_rhr()``, ``reverse_geom()``, and ``geohash()`` ``GeoQuerySet``
|
||||
methods are new.
|
||||
|
||||
The GEOS interface was updated to use thread-safe C library functions when
|
||||
available on the platform.
|
||||
|
|
|
@ -261,3 +261,5 @@ these features.
|
|||
ORM, e.g. with ``cursor.execute()``.
|
||||
|
||||
* ``django.contrib.auth.tests.utils.skipIfCustomUser()`` is removed.
|
||||
|
||||
* The ``GeoManager`` and ``GeoQuerySet`` classes are removed.
|
||||
|
|
|
@ -8,8 +8,6 @@ from ..utils import gisfield_may_be_null
|
|||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
required_db_features = ['gis_enabled']
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from unittest import skipIf
|
||||
|
||||
from django.contrib.gis.db.models.functions import (
|
||||
Area, Distance, Length, Perimeter, Transform,
|
||||
)
|
||||
|
@ -9,8 +7,7 @@ from django.contrib.gis.geos import GEOSGeometry, LineString, Point
|
|||
from django.contrib.gis.measure import D # alias for Distance
|
||||
from django.db import connection
|
||||
from django.db.models import F, Q
|
||||
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
|
||||
from ..utils import no_oracle, oracle, postgis, spatialite
|
||||
from .models import (
|
||||
|
@ -101,136 +98,6 @@ class DistanceTest(TestCase):
|
|||
else:
|
||||
self.assertListEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist))))
|
||||
|
||||
@skipUnlessDBFeature("has_distance_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_distance_projected(self):
|
||||
"""
|
||||
Test the `distance` GeoQuerySet method on projected coordinate systems.
|
||||
"""
|
||||
# The point for La Grange, TX
|
||||
lagrange = GEOSGeometry('POINT(-96.876369 29.905320)', 4326)
|
||||
# Reference distances in feet and in meters. Got these values from
|
||||
# using the provided raw SQL statements.
|
||||
# SELECT ST_Distance(point, ST_Transform(ST_GeomFromText('POINT(-96.876369 29.905320)', 4326), 32140))
|
||||
# FROM distapp_southtexascity;
|
||||
m_distances = [147075.069813, 139630.198056, 140888.552826,
|
||||
138809.684197, 158309.246259, 212183.594374,
|
||||
70870.188967, 165337.758878, 139196.085105]
|
||||
# SELECT ST_Distance(point, ST_Transform(ST_GeomFromText('POINT(-96.876369 29.905320)', 4326), 2278))
|
||||
# FROM distapp_southtexascityft;
|
||||
# Oracle 11 thinks this is not a projected coordinate system, so it's
|
||||
# not tested.
|
||||
ft_distances = [482528.79154625, 458103.408123001, 462231.860397575,
|
||||
455411.438904354, 519386.252102563, 696139.009211594,
|
||||
232513.278304279, 542445.630586414, 456679.155883207]
|
||||
|
||||
# Testing using different variations of parameters and using models
|
||||
# with different projected coordinate systems.
|
||||
dist1 = SouthTexasCity.objects.distance(lagrange, field_name='point').order_by('id')
|
||||
dist2 = SouthTexasCity.objects.distance(lagrange).order_by('id') # Using GEOSGeometry parameter
|
||||
if oracle:
|
||||
dist_qs = [dist1, dist2]
|
||||
else:
|
||||
dist3 = SouthTexasCityFt.objects.distance(lagrange.ewkt).order_by('id') # Using EWKT string parameter.
|
||||
dist4 = SouthTexasCityFt.objects.distance(lagrange).order_by('id')
|
||||
dist_qs = [dist1, dist2, dist3, dist4]
|
||||
|
||||
# Original query done on PostGIS, have to adjust AlmostEqual tolerance
|
||||
# for Oracle.
|
||||
tol = 2 if oracle else 5
|
||||
|
||||
# Ensuring expected distances are returned for each distance queryset.
|
||||
for qs in dist_qs:
|
||||
for i, c in enumerate(qs):
|
||||
self.assertAlmostEqual(m_distances[i], c.distance.m, tol)
|
||||
self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
|
||||
|
||||
@skipIf(spatialite, "distance method doesn't support geodetic coordinates on SpatiaLite.")
|
||||
@skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_distance_geodetic(self):
|
||||
"""
|
||||
Test the `distance` GeoQuerySet method on geodetic coordinate systems.
|
||||
"""
|
||||
tol = 2 if oracle else 4
|
||||
|
||||
# Testing geodetic distance calculation with a non-point geometry
|
||||
# (a LineString of Wollongong and Shellharbour coords).
|
||||
ls = LineString(((150.902, -34.4245), (150.87, -34.5789)))
|
||||
|
||||
# Reference query:
|
||||
# SELECT ST_distance_sphere(point, ST_GeomFromText('LINESTRING(150.9020 -34.4245,150.8700 -34.5789)', 4326))
|
||||
# FROM distapp_australiacity ORDER BY name;
|
||||
distances = [1120954.92533513, 140575.720018241, 640396.662906304,
|
||||
60580.9693849269, 972807.955955075, 568451.8357838,
|
||||
40435.4335201384, 0, 68272.3896586844, 12375.0643697706, 0]
|
||||
qs = AustraliaCity.objects.distance(ls).order_by('name')
|
||||
for city, distance in zip(qs, distances):
|
||||
# Testing equivalence to within a meter.
|
||||
self.assertAlmostEqual(distance, city.distance.m, 0)
|
||||
|
||||
# Got the reference distances using the raw SQL statements:
|
||||
# SELECT ST_distance_spheroid(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326),
|
||||
# 'SPHEROID["WGS 84",6378137.0,298.257223563]') FROM distapp_australiacity WHERE (NOT (id = 11));
|
||||
# SELECT ST_distance_sphere(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326))
|
||||
# FROM distapp_australiacity WHERE (NOT (id = 11)); st_distance_sphere
|
||||
spheroid_distances = [
|
||||
60504.0628957201, 77023.9489850262, 49154.8867574404,
|
||||
90847.4358768573, 217402.811919332, 709599.234564757,
|
||||
640011.483550888, 7772.00667991925, 1047861.78619339,
|
||||
1165126.55236034,
|
||||
]
|
||||
sphere_distances = [
|
||||
60580.9693849267, 77144.0435286473, 49199.4415344719,
|
||||
90804.7533823494, 217713.384600405, 709134.127242793,
|
||||
639828.157159169, 7786.82949717788, 1049204.06569028,
|
||||
1162623.7238134,
|
||||
]
|
||||
# Testing with spheroid distances first.
|
||||
hillsdale = AustraliaCity.objects.get(name='Hillsdale')
|
||||
qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point, spheroid=True).order_by('id')
|
||||
for i, c in enumerate(qs):
|
||||
self.assertAlmostEqual(spheroid_distances[i], c.distance.m, tol)
|
||||
if postgis:
|
||||
# PostGIS uses sphere-only distances by default, testing these as well.
|
||||
qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point).order_by('id')
|
||||
for i, c in enumerate(qs):
|
||||
self.assertAlmostEqual(sphere_distances[i], c.distance.m, tol)
|
||||
|
||||
@no_oracle # Oracle already handles geographic distance calculation.
|
||||
@skipUnlessDBFeature("has_distance_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_distance_transform(self):
|
||||
"""
|
||||
Test the `distance` GeoQuerySet method used with `transform` on a geographic field.
|
||||
"""
|
||||
# We'll be using a Polygon (created by buffering the centroid
|
||||
# of 77005 to 100m) -- which aren't allowed in geographic distance
|
||||
# queries normally, however our field has been transformed to
|
||||
# a non-geographic system.
|
||||
z = SouthTexasZipcode.objects.get(name='77005')
|
||||
|
||||
# Reference query:
|
||||
# SELECT ST_Distance(ST_Transform("distapp_censuszipcode"."poly", 32140),
|
||||
# ST_GeomFromText('<buffer_wkt>', 32140))
|
||||
# FROM "distapp_censuszipcode";
|
||||
dists_m = [3553.30384972258, 1243.18391525602, 2186.15439472242]
|
||||
|
||||
# Having our buffer in the SRID of the transformation and of the field
|
||||
# -- should get the same results. The first buffer has no need for
|
||||
# transformation SQL because it is the same SRID as what was given
|
||||
# to `transform()`. The second buffer will need to be transformed,
|
||||
# however.
|
||||
buf1 = z.poly.centroid.buffer(100)
|
||||
buf2 = buf1.transform(4269, clone=True)
|
||||
ref_zips = ['77002', '77025', '77401']
|
||||
|
||||
for buf in [buf1, buf2]:
|
||||
qs = CensusZipcode.objects.exclude(name='77005').transform(32140).distance(buf).order_by('name')
|
||||
self.assertListEqual(ref_zips, self.get_names(qs))
|
||||
for i, z in enumerate(qs):
|
||||
self.assertAlmostEqual(z.distance.m, dists_m[i], 5)
|
||||
|
||||
@skipUnlessDBFeature("supports_distances_lookups")
|
||||
def test_distance_lookups(self):
|
||||
"""
|
||||
|
@ -347,86 +214,6 @@ class DistanceTest(TestCase):
|
|||
).order_by('name')
|
||||
self.assertEqual(self.get_names(qs), ['Canberra', 'Hobart', 'Melbourne'])
|
||||
|
||||
@skipUnlessDBFeature("has_area_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_area(self):
|
||||
"""
|
||||
Test the `area` GeoQuerySet method.
|
||||
"""
|
||||
# Reference queries:
|
||||
# SELECT ST_Area(poly) FROM distapp_southtexaszipcode;
|
||||
area_sq_m = [5437908.90234375, 10183031.4389648, 11254471.0073242, 9881708.91772461]
|
||||
# Tolerance has to be lower for Oracle
|
||||
tol = 2
|
||||
for i, z in enumerate(SouthTexasZipcode.objects.order_by('name').area()):
|
||||
self.assertAlmostEqual(area_sq_m[i], z.area.sq_m, tol)
|
||||
|
||||
@skipIf(spatialite, "length method doesn't support geodetic coordinates on SpatiaLite.")
|
||||
@skipUnlessDBFeature("has_length_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_length(self):
|
||||
"""
|
||||
Test the `length` GeoQuerySet method.
|
||||
"""
|
||||
# Reference query (should use `length_spheroid`).
|
||||
# SELECT ST_length_spheroid(ST_GeomFromText('<wkt>', 4326) 'SPHEROID["WGS 84",6378137,298.257223563,
|
||||
# AUTHORITY["EPSG","7030"]]');
|
||||
len_m1 = 473504.769553813
|
||||
len_m2 = 4617.668
|
||||
|
||||
if connection.features.supports_distance_geodetic:
|
||||
qs = Interstate.objects.length()
|
||||
tol = 2 if oracle else 3
|
||||
self.assertAlmostEqual(len_m1, qs[0].length.m, tol)
|
||||
else:
|
||||
# Does not support geodetic coordinate systems.
|
||||
with self.assertRaises(ValueError):
|
||||
Interstate.objects.length()
|
||||
|
||||
# Now doing length on a projected coordinate system.
|
||||
i10 = SouthTexasInterstate.objects.length().get(name='I-10')
|
||||
self.assertAlmostEqual(len_m2, i10.length.m, 2)
|
||||
|
||||
@skipUnlessDBFeature("has_perimeter_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_perimeter(self):
|
||||
"""
|
||||
Test the `perimeter` GeoQuerySet method.
|
||||
"""
|
||||
# Reference query:
|
||||
# SELECT ST_Perimeter(distapp_southtexaszipcode.poly) FROM distapp_southtexaszipcode;
|
||||
perim_m = [18404.3550889361, 15627.2108551001, 20632.5588368978, 17094.5996143697]
|
||||
tol = 2 if oracle else 7
|
||||
for i, z in enumerate(SouthTexasZipcode.objects.order_by('name').perimeter()):
|
||||
self.assertAlmostEqual(perim_m[i], z.perimeter.m, tol)
|
||||
|
||||
# Running on points; should return 0.
|
||||
for i, c in enumerate(SouthTexasCity.objects.perimeter(model_att='perim')):
|
||||
self.assertEqual(0, c.perim.m)
|
||||
|
||||
@skipUnlessDBFeature("has_area_method", "has_distance_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_measurement_null_fields(self):
|
||||
"""
|
||||
Test the measurement GeoQuerySet methods on fields with NULL values.
|
||||
"""
|
||||
# Creating SouthTexasZipcode w/NULL value.
|
||||
SouthTexasZipcode.objects.create(name='78212')
|
||||
# Performing distance/area queries against the NULL PolygonField,
|
||||
# and ensuring the result of the operations is None.
|
||||
htown = SouthTexasCity.objects.get(name='Downtown Houston')
|
||||
z = SouthTexasZipcode.objects.distance(htown.point).area().get(name='78212')
|
||||
self.assertIsNone(z.distance)
|
||||
self.assertIsNone(z.area)
|
||||
|
||||
@skipUnlessDBFeature("has_distance_method")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_distance_order_by(self):
|
||||
qs = SouthTexasCity.objects.distance(Point(3, 3)).order_by(
|
||||
'distance'
|
||||
).values_list('name', flat=True).filter(name__in=('San Antonio', 'Pearland'))
|
||||
self.assertSequenceEqual(qs, ['San Antonio', 'Pearland'])
|
||||
|
||||
|
||||
'''
|
||||
=============================
|
||||
|
|
|
@ -6,8 +6,6 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
required_db_features = ['gis_enabled']
|
||||
|
|
|
@ -8,9 +8,8 @@ from django.contrib.gis.db.models.functions import (
|
|||
AsGeoJSON, AsKML, Length, Perimeter, Scale, Translate,
|
||||
)
|
||||
from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon
|
||||
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
from django.utils._os import upath
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
|
||||
from .models import (
|
||||
City3D, Interstate2D, Interstate3D, InterstateProj2D, InterstateProj3D,
|
||||
|
@ -171,30 +170,6 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
|
|||
lm.save()
|
||||
self.assertEqual(3, MultiPoint3D.objects.count())
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_kml(self):
|
||||
"""
|
||||
Test GeoQuerySet.kml() with Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
h = City3D.objects.kml(precision=6).get(name='Houston')
|
||||
# KML should be 3D.
|
||||
# `SELECT ST_AsKML(point, 6) FROM geo3d_city3d WHERE name = 'Houston';`
|
||||
ref_kml_regex = re.compile(r'^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$')
|
||||
self.assertTrue(ref_kml_regex.match(h.kml))
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test_geojson(self):
|
||||
"""
|
||||
Test GeoQuerySet.geojson() with Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
h = City3D.objects.geojson(precision=6).get(name='Houston')
|
||||
# GeoJSON should be 3D
|
||||
# `SELECT ST_AsGeoJSON(point, 6) FROM geo3d_city3d WHERE name='Houston';`
|
||||
ref_json_regex = re.compile(r'^{"type":"Point","coordinates":\[-95.363151,29.763374,18(\.0+)?\]}$')
|
||||
self.assertTrue(ref_json_regex.match(h.geojson))
|
||||
|
||||
@skipUnlessDBFeature("supports_3d_functions")
|
||||
def test_union(self):
|
||||
"""
|
||||
|
@ -231,84 +206,6 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
|
|||
check_extent3d(extent)
|
||||
self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d'])
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
@skipUnlessDBFeature("supports_3d_functions")
|
||||
def test_perimeter(self):
|
||||
"""
|
||||
Testing GeoQuerySet.perimeter() on 3D fields.
|
||||
"""
|
||||
self._load_polygon_data()
|
||||
# Reference query for values below:
|
||||
# `SELECT ST_Perimeter3D(poly), ST_Perimeter2D(poly) FROM geo3d_polygon3d;`
|
||||
ref_perim_3d = 76859.2620451
|
||||
ref_perim_2d = 76859.2577803
|
||||
tol = 6
|
||||
self.assertAlmostEqual(ref_perim_2d,
|
||||
Polygon2D.objects.perimeter().get(name='2D BBox').perimeter.m,
|
||||
tol)
|
||||
self.assertAlmostEqual(ref_perim_3d,
|
||||
Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m,
|
||||
tol)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
@skipUnlessDBFeature("supports_3d_functions")
|
||||
def test_length(self):
|
||||
"""
|
||||
Testing GeoQuerySet.length() on 3D fields.
|
||||
"""
|
||||
# ST_Length_Spheroid Z-aware, and thus does not need to use
|
||||
# a separate function internally.
|
||||
# `SELECT ST_Length_Spheroid(line, 'SPHEROID["GRS 1980",6378137,298.257222101]')
|
||||
# FROM geo3d_interstate[2d|3d];`
|
||||
self._load_interstate_data()
|
||||
tol = 3
|
||||
ref_length_2d = 4368.1721949481
|
||||
ref_length_3d = 4368.62547052088
|
||||
self.assertAlmostEqual(ref_length_2d,
|
||||
Interstate2D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
self.assertAlmostEqual(ref_length_3d,
|
||||
Interstate3D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
|
||||
# Making sure `ST_Length3D` is used on for a projected
|
||||
# and 3D model rather than `ST_Length`.
|
||||
# `SELECT ST_Length(line) FROM geo3d_interstateproj2d;`
|
||||
ref_length_2d = 4367.71564892392
|
||||
# `SELECT ST_Length3D(line) FROM geo3d_interstateproj3d;`
|
||||
ref_length_3d = 4368.16897234101
|
||||
self.assertAlmostEqual(ref_length_2d,
|
||||
InterstateProj2D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
self.assertAlmostEqual(ref_length_3d,
|
||||
InterstateProj3D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
@skipUnlessDBFeature("supports_3d_functions")
|
||||
def test_scale(self):
|
||||
"""
|
||||
Testing GeoQuerySet.scale() on Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
# Mapping of City name to reference Z values.
|
||||
zscales = (-3, 4, 23)
|
||||
for zscale in zscales:
|
||||
for city in City3D.objects.scale(1.0, 1.0, zscale):
|
||||
self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
@skipUnlessDBFeature("supports_3d_functions")
|
||||
def test_translate(self):
|
||||
"""
|
||||
Testing GeoQuerySet.translate() on Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
ztranslations = (5.23, 23, -17)
|
||||
for ztrans in ztranslations:
|
||||
for city in City3D.objects.translate(0, 0, ztrans):
|
||||
self.assertEqual(city_dict[city.name][2] + ztrans, city.translate.z)
|
||||
|
||||
|
||||
@skipUnlessDBFeature("gis_enabled", "supports_3d_functions")
|
||||
class Geo3DFunctionsTests(Geo3DLoadingHelper, TestCase):
|
||||
|
|
|
@ -8,8 +8,6 @@ from ..utils import gisfield_may_be_null
|
|||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
required_db_features = ['gis_enabled']
|
||||
|
|
|
@ -19,7 +19,6 @@ from .models import City, Country, CountryWebMercator, State, Track
|
|||
class GISFunctionsTests(TestCase):
|
||||
"""
|
||||
Testing functions from django/contrib/gis/db/models/functions.py.
|
||||
Several tests are taken and adapted from GeoQuerySetTest.
|
||||
Area/Distance/Length/Perimeter are tested in distapp/tests.
|
||||
|
||||
Please keep the tests in function's alphabetic order.
|
||||
|
@ -462,7 +461,6 @@ class GISFunctionsTests(TestCase):
|
|||
"has_Difference_function", "has_Intersection_function",
|
||||
"has_SymDifference_function", "has_Union_function")
|
||||
def test_diff_intersection_union(self):
|
||||
"Testing the `difference`, `intersection`, `sym_difference`, and `union` GeoQuerySet methods."
|
||||
geom = Point(5, 23, srid=4326)
|
||||
qs = Country.objects.all().annotate(
|
||||
difference=functions.Difference('mpoly', geom),
|
||||
|
|
|
@ -17,7 +17,7 @@ class GeoRegressionTests(TestCase):
|
|||
fixtures = ['initial']
|
||||
|
||||
def test_update(self):
|
||||
"Testing GeoQuerySet.update(). See #10411."
|
||||
"Testing QuerySet.update() (#10411)."
|
||||
pnt = City.objects.get(name='Pueblo').point
|
||||
bak = pnt.clone()
|
||||
pnt.y += 0.005
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.db.models import Extent, MakeLine, Union
|
||||
from django.contrib.gis.db.models import Extent, MakeLine, Union, functions
|
||||
from django.contrib.gis.geos import (
|
||||
GeometryCollection, GEOSGeometry, LinearRing, LineString, MultiLineString,
|
||||
MultiPoint, MultiPolygon, Point, Polygon, fromstr,
|
||||
)
|
||||
from django.core.management import call_command
|
||||
from django.db import connection
|
||||
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
|
||||
from ..utils import oracle, postgis, skipUnlessGISLookup, spatialite
|
||||
from ..utils import no_oracle, oracle, postgis, skipUnlessGISLookup, spatialite
|
||||
from .models import (
|
||||
City, Country, Feature, MinusOneSRID, NonConcreteModel, PennsylvaniaCity,
|
||||
State, Track,
|
||||
|
@ -155,19 +153,22 @@ class GeoModelTest(TestCase):
|
|||
self.assertIsInstance(f_4.geom, GeometryCollection)
|
||||
self.assertEqual(f_3.geom, f_4.geom[2])
|
||||
|
||||
# TODO: fix on Oracle: ORA-22901: cannot compare nested table or VARRAY or
|
||||
# LOB attributes of an object type.
|
||||
@no_oracle
|
||||
@skipUnlessDBFeature("supports_transform")
|
||||
def test_inherited_geofields(self):
|
||||
"Test GeoQuerySet methods on inherited Geometry fields."
|
||||
"Database functions on inherited Geometry fields."
|
||||
# Creating a Pennsylvanian city.
|
||||
PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)')
|
||||
|
||||
# All transformation SQL will need to be performed on the
|
||||
# _parent_ table.
|
||||
qs = PennsylvaniaCity.objects.transform(32128)
|
||||
qs = PennsylvaniaCity.objects.annotate(new_point=functions.Transform('point', srid=32128))
|
||||
|
||||
self.assertEqual(1, qs.count())
|
||||
for pc in qs:
|
||||
self.assertEqual(32128, pc.point.srid)
|
||||
self.assertEqual(32128, pc.new_point.srid)
|
||||
|
||||
def test_raw_sql_query(self):
|
||||
"Testing raw SQL query."
|
||||
|
@ -412,8 +413,8 @@ class GeoLookupTest(TestCase):
|
|||
pnt1 = fromstr('POINT (649287.0363174 4177429.4494686)', srid=2847)
|
||||
pnt2 = fromstr('POINT(-98.4919715741052 29.4333344025053)', srid=4326)
|
||||
|
||||
# Not passing in a geometry as first param should
|
||||
# raise a type error when initializing the GeoQuerySet
|
||||
# Not passing in a geometry as first param raises a TypeError when
|
||||
# initializing the QuerySet.
|
||||
with self.assertRaises(ValueError):
|
||||
Country.objects.filter(mpoly__relate=(23, 'foo'))
|
||||
|
||||
|
@ -451,66 +452,10 @@ class GeoLookupTest(TestCase):
|
|||
|
||||
|
||||
@skipUnlessDBFeature("gis_enabled")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
class GeoQuerySetTest(TestCase):
|
||||
# TODO: GeoQuerySet is removed, organize these test better.
|
||||
fixtures = ['initial']
|
||||
|
||||
# Please keep the tests in GeoQuerySet method's alphabetic order
|
||||
|
||||
@skipUnlessDBFeature("has_centroid_method")
|
||||
def test_centroid(self):
|
||||
"Testing the `centroid` GeoQuerySet method."
|
||||
qs = State.objects.exclude(poly__isnull=True).centroid()
|
||||
if oracle:
|
||||
tol = 0.1
|
||||
elif spatialite:
|
||||
tol = 0.000001
|
||||
else:
|
||||
tol = 0.000000001
|
||||
for s in qs:
|
||||
self.assertTrue(s.poly.centroid.equals_exact(s.centroid, tol))
|
||||
|
||||
@skipUnlessDBFeature(
|
||||
"has_difference_method", "has_intersection_method",
|
||||
"has_sym_difference_method", "has_union_method")
|
||||
def test_diff_intersection_union(self):
|
||||
"Testing the `difference`, `intersection`, `sym_difference`, and `union` GeoQuerySet methods."
|
||||
geom = Point(5, 23)
|
||||
qs = Country.objects.all().difference(geom).sym_difference(geom).union(geom)
|
||||
|
||||
# XXX For some reason SpatiaLite does something screwy with the Texas geometry here. Also,
|
||||
# XXX it doesn't like the null intersection.
|
||||
if spatialite:
|
||||
qs = qs.exclude(name='Texas')
|
||||
else:
|
||||
qs = qs.intersection(geom)
|
||||
|
||||
for c in qs:
|
||||
if oracle:
|
||||
# Should be able to execute the queries; however, they won't be the same
|
||||
# as GEOS (because Oracle doesn't use GEOS internally like PostGIS or
|
||||
# SpatiaLite).
|
||||
pass
|
||||
else:
|
||||
if spatialite:
|
||||
# Spatialite `difference` doesn't have an SRID
|
||||
self.assertEqual(c.mpoly.difference(geom).wkt, c.difference.wkt)
|
||||
else:
|
||||
self.assertEqual(c.mpoly.difference(geom), c.difference)
|
||||
self.assertEqual(c.mpoly.intersection(geom), c.intersection)
|
||||
# Ordering might differ in collections
|
||||
self.assertSetEqual(set(g.wkt for g in c.mpoly.sym_difference(geom)),
|
||||
set(g.wkt for g in c.sym_difference))
|
||||
self.assertSetEqual(set(g.wkt for g in c.mpoly.union(geom)),
|
||||
set(g.wkt for g in c.union))
|
||||
|
||||
@skipUnlessDBFeature("has_envelope_method")
|
||||
def test_envelope(self):
|
||||
"Testing the `envelope` GeoQuerySet method."
|
||||
countries = Country.objects.all().envelope()
|
||||
for country in countries:
|
||||
self.assertIsInstance(country.envelope, Polygon)
|
||||
|
||||
@skipUnlessDBFeature("supports_extent_aggr")
|
||||
def test_extent(self):
|
||||
"""
|
||||
|
@ -536,132 +481,6 @@ class GeoQuerySetTest(TestCase):
|
|||
extent2 = City.objects.all()[:3].aggregate(Extent('point'))['point__extent']
|
||||
self.assertNotEqual(extent1, extent2)
|
||||
|
||||
@skipUnlessDBFeature("has_force_rhr_method")
|
||||
def test_force_rhr(self):
|
||||
"Testing GeoQuerySet.force_rhr()."
|
||||
rings = (
|
||||
((0, 0), (5, 0), (0, 5), (0, 0)),
|
||||
((1, 1), (1, 3), (3, 1), (1, 1)),
|
||||
)
|
||||
rhr_rings = (
|
||||
((0, 0), (0, 5), (5, 0), (0, 0)),
|
||||
((1, 1), (3, 1), (1, 3), (1, 1)),
|
||||
)
|
||||
State.objects.create(name='Foo', poly=Polygon(*rings))
|
||||
s = State.objects.force_rhr().get(name='Foo')
|
||||
self.assertEqual(rhr_rings, s.force_rhr.coords)
|
||||
|
||||
@skipUnlessDBFeature("has_geohash_method")
|
||||
def test_geohash(self):
|
||||
"Testing GeoQuerySet.geohash()."
|
||||
# Reference query:
|
||||
# SELECT ST_GeoHash(point) FROM geoapp_city WHERE name='Houston';
|
||||
# SELECT ST_GeoHash(point, 5) FROM geoapp_city WHERE name='Houston';
|
||||
ref_hash = '9vk1mfq8jx0c8e0386z6'
|
||||
h1 = City.objects.geohash().get(name='Houston')
|
||||
h2 = City.objects.geohash(precision=5).get(name='Houston')
|
||||
self.assertEqual(ref_hash, h1.geohash)
|
||||
self.assertEqual(ref_hash[:5], h2.geohash)
|
||||
|
||||
def test_geojson(self):
|
||||
"Testing GeoJSON output from the database using GeoQuerySet.geojson()."
|
||||
# Only PostGIS and SpatiaLite support GeoJSON.
|
||||
if not connection.ops.geojson:
|
||||
with self.assertRaises(NotImplementedError):
|
||||
Country.objects.all().geojson(field_name='mpoly')
|
||||
return
|
||||
|
||||
pueblo_json = '{"type":"Point","coordinates":[-104.609252,38.255001]}'
|
||||
houston_json = (
|
||||
'{"type":"Point","crs":{"type":"name","properties":'
|
||||
'{"name":"EPSG:4326"}},"coordinates":[-95.363151,29.763374]}'
|
||||
)
|
||||
victoria_json = (
|
||||
'{"type":"Point","bbox":[-123.30519600,48.46261100,-123.30519600,48.46261100],'
|
||||
'"coordinates":[-123.305196,48.462611]}'
|
||||
)
|
||||
chicago_json = (
|
||||
'{"type":"Point","crs":{"type":"name","properties":{"name":"EPSG:4326"}},'
|
||||
'"bbox":[-87.65018,41.85039,-87.65018,41.85039],"coordinates":[-87.65018,41.85039]}'
|
||||
)
|
||||
if spatialite:
|
||||
victoria_json = (
|
||||
'{"type":"Point","bbox":[-123.305196,48.462611,-123.305196,48.462611],'
|
||||
'"coordinates":[-123.305196,48.462611]}'
|
||||
)
|
||||
|
||||
# Precision argument should only be an integer
|
||||
with self.assertRaises(TypeError):
|
||||
City.objects.geojson(precision='foo')
|
||||
|
||||
# Reference queries and values.
|
||||
# SELECT ST_AsGeoJson("geoapp_city"."point", 8, 0)
|
||||
# FROM "geoapp_city" WHERE "geoapp_city"."name" = 'Pueblo';
|
||||
self.assertEqual(pueblo_json, City.objects.geojson().get(name='Pueblo').geojson)
|
||||
|
||||
# SELECT ST_AsGeoJson("geoapp_city"."point", 8, 2) FROM "geoapp_city"
|
||||
# WHERE "geoapp_city"."name" = 'Houston';
|
||||
# This time we want to include the CRS by using the `crs` keyword.
|
||||
self.assertEqual(houston_json, City.objects.geojson(crs=True, model_att='json').get(name='Houston').json)
|
||||
|
||||
# SELECT ST_AsGeoJson("geoapp_city"."point", 8, 1) FROM "geoapp_city"
|
||||
# WHERE "geoapp_city"."name" = 'Houston';
|
||||
# This time we include the bounding box by using the `bbox` keyword.
|
||||
self.assertEqual(victoria_json, City.objects.geojson(bbox=True).get(name='Victoria').geojson)
|
||||
|
||||
# SELECT ST_AsGeoJson("geoapp_city"."point", 5, 3) FROM "geoapp_city"
|
||||
# WHERE "geoapp_city"."name" = 'Chicago';
|
||||
# Finally, we set every available keyword.
|
||||
self.assertEqual(
|
||||
chicago_json,
|
||||
City.objects.geojson(bbox=True, crs=True, precision=5).get(name='Chicago').geojson
|
||||
)
|
||||
|
||||
@skipUnlessDBFeature("has_gml_method")
|
||||
def test_gml(self):
|
||||
"Testing GML output from the database using GeoQuerySet.gml()."
|
||||
# Should throw a TypeError when trying to obtain GML from a
|
||||
# non-geometry field.
|
||||
qs = City.objects.all()
|
||||
with self.assertRaises(TypeError):
|
||||
qs.gml(field_name='name')
|
||||
ptown1 = City.objects.gml(field_name='point', precision=9).get(name='Pueblo')
|
||||
ptown2 = City.objects.gml(precision=9).get(name='Pueblo')
|
||||
|
||||
if oracle:
|
||||
# No precision parameter for Oracle :-/
|
||||
gml_regex = re.compile(
|
||||
r'^<gml:Point srsName="EPSG:4326" xmlns:gml="http://www.opengis.net/gml">'
|
||||
r'<gml:coordinates decimal="\." cs="," ts=" ">-104.60925\d+,38.25500\d+ '
|
||||
r'</gml:coordinates></gml:Point>'
|
||||
)
|
||||
else:
|
||||
gml_regex = re.compile(
|
||||
r'^<gml:Point srsName="EPSG:4326"><gml:coordinates>'
|
||||
r'-104\.60925\d+,38\.255001</gml:coordinates></gml:Point>'
|
||||
)
|
||||
|
||||
for ptown in [ptown1, ptown2]:
|
||||
self.assertTrue(gml_regex.match(ptown.gml))
|
||||
|
||||
if postgis:
|
||||
self.assertIn('<gml:pos srsDimension="2">', City.objects.gml(version=3).get(name='Pueblo').gml)
|
||||
|
||||
@skipUnlessDBFeature("has_kml_method")
|
||||
def test_kml(self):
|
||||
"Testing KML output from the database using GeoQuerySet.kml()."
|
||||
# Should throw a TypeError when trying to obtain KML from a
|
||||
# non-geometry field.
|
||||
qs = City.objects.all()
|
||||
with self.assertRaises(TypeError):
|
||||
qs.kml('name')
|
||||
|
||||
# Ensuring the KML is as expected.
|
||||
ptown1 = City.objects.kml(field_name='point', precision=9).get(name='Pueblo')
|
||||
ptown2 = City.objects.kml(precision=9).get(name='Pueblo')
|
||||
for ptown in [ptown1, ptown2]:
|
||||
self.assertEqual('<Point><coordinates>-104.609252,38.255001</coordinates></Point>', ptown.kml)
|
||||
|
||||
def test_make_line(self):
|
||||
"""
|
||||
Testing the `MakeLine` aggregate.
|
||||
|
@ -689,181 +508,6 @@ class GeoQuerySetTest(TestCase):
|
|||
"%s != %s" % (ref_line, line)
|
||||
)
|
||||
|
||||
@skipUnlessDBFeature("has_num_geom_method")
|
||||
def test_num_geom(self):
|
||||
"Testing the `num_geom` GeoQuerySet method."
|
||||
# Both 'countries' only have two geometries.
|
||||
for c in Country.objects.num_geom():
|
||||
self.assertEqual(2, c.num_geom)
|
||||
|
||||
for c in City.objects.filter(point__isnull=False).num_geom():
|
||||
# Oracle and PostGIS 2.0+ will return 1 for the number of
|
||||
# geometries on non-collections.
|
||||
self.assertEqual(1, c.num_geom)
|
||||
|
||||
@skipUnlessDBFeature("supports_num_points_poly")
|
||||
def test_num_points(self):
|
||||
"Testing the `num_points` GeoQuerySet method."
|
||||
for c in Country.objects.num_points():
|
||||
self.assertEqual(c.mpoly.num_points, c.num_points)
|
||||
|
||||
if not oracle:
|
||||
# Oracle cannot count vertices in Point geometries.
|
||||
for c in City.objects.num_points():
|
||||
self.assertEqual(1, c.num_points)
|
||||
|
||||
@skipUnlessDBFeature("has_point_on_surface_method")
|
||||
def test_point_on_surface(self):
|
||||
"Testing the `point_on_surface` GeoQuerySet method."
|
||||
# Reference values.
|
||||
if oracle:
|
||||
# SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_GEOM.SDO_POINTONSURFACE(GEOAPP_COUNTRY.MPOLY, 0.05))
|
||||
# FROM GEOAPP_COUNTRY;
|
||||
ref = {'New Zealand': fromstr('POINT (174.616364 -36.100861)', srid=4326),
|
||||
'Texas': fromstr('POINT (-103.002434 36.500397)', srid=4326),
|
||||
}
|
||||
|
||||
else:
|
||||
# Using GEOSGeometry to compute the reference point on surface values
|
||||
# -- since PostGIS also uses GEOS these should be the same.
|
||||
ref = {'New Zealand': Country.objects.get(name='New Zealand').mpoly.point_on_surface,
|
||||
'Texas': Country.objects.get(name='Texas').mpoly.point_on_surface
|
||||
}
|
||||
|
||||
for c in Country.objects.point_on_surface():
|
||||
if spatialite:
|
||||
# XXX This seems to be a WKT-translation-related precision issue?
|
||||
tol = 0.00001
|
||||
else:
|
||||
tol = 0.000000001
|
||||
self.assertTrue(ref[c.name].equals_exact(c.point_on_surface, tol))
|
||||
|
||||
@skipUnlessDBFeature("has_reverse_method")
|
||||
def test_reverse_geom(self):
|
||||
"Testing GeoQuerySet.reverse_geom()."
|
||||
coords = [(-95.363151, 29.763374), (-95.448601, 29.713803)]
|
||||
Track.objects.create(name='Foo', line=LineString(coords))
|
||||
t = Track.objects.reverse_geom().get(name='Foo')
|
||||
coords.reverse()
|
||||
self.assertEqual(tuple(coords), t.reverse_geom.coords)
|
||||
if oracle:
|
||||
with self.assertRaises(TypeError):
|
||||
State.objects.reverse_geom()
|
||||
|
||||
@skipUnlessDBFeature("has_scale_method")
|
||||
def test_scale(self):
|
||||
"Testing the `scale` GeoQuerySet method."
|
||||
xfac, yfac = 2, 3
|
||||
tol = 5 # XXX The low precision tolerance is for SpatiaLite
|
||||
qs = Country.objects.scale(xfac, yfac, model_att='scaled')
|
||||
for c in qs:
|
||||
for p1, p2 in zip(c.mpoly, c.scaled):
|
||||
for r1, r2 in zip(p1, p2):
|
||||
for c1, c2 in zip(r1.coords, r2.coords):
|
||||
self.assertAlmostEqual(c1[0] * xfac, c2[0], tol)
|
||||
self.assertAlmostEqual(c1[1] * yfac, c2[1], tol)
|
||||
|
||||
@skipUnlessDBFeature("has_snap_to_grid_method")
|
||||
def test_snap_to_grid(self):
|
||||
"Testing GeoQuerySet.snap_to_grid()."
|
||||
# Let's try and break snap_to_grid() with bad combinations of arguments.
|
||||
for bad_args in ((), range(3), range(5)):
|
||||
with self.assertRaises(ValueError):
|
||||
Country.objects.snap_to_grid(*bad_args)
|
||||
for bad_args in (('1.0',), (1.0, None), tuple(map(six.text_type, range(4)))):
|
||||
with self.assertRaises(TypeError):
|
||||
Country.objects.snap_to_grid(*bad_args)
|
||||
|
||||
# Boundary for San Marino, courtesy of Bjorn Sandvik of thematicmapping.org
|
||||
# from the world borders dataset he provides.
|
||||
wkt = ('MULTIPOLYGON(((12.41580 43.95795,12.45055 43.97972,12.45389 43.98167,'
|
||||
'12.46250 43.98472,12.47167 43.98694,12.49278 43.98917,'
|
||||
'12.50555 43.98861,12.51000 43.98694,12.51028 43.98277,'
|
||||
'12.51167 43.94333,12.51056 43.93916,12.49639 43.92333,'
|
||||
'12.49500 43.91472,12.48778 43.90583,12.47444 43.89722,'
|
||||
'12.46472 43.89555,12.45917 43.89611,12.41639 43.90472,'
|
||||
'12.41222 43.90610,12.40782 43.91366,12.40389 43.92667,'
|
||||
'12.40500 43.94833,12.40889 43.95499,12.41580 43.95795)))')
|
||||
Country.objects.create(name='San Marino', mpoly=fromstr(wkt))
|
||||
|
||||
# Because floating-point arithmetic isn't exact, we set a tolerance
|
||||
# to pass into GEOS `equals_exact`.
|
||||
tol = 0.000000001
|
||||
|
||||
# SELECT AsText(ST_SnapToGrid("geoapp_country"."mpoly", 0.1)) FROM "geoapp_country"
|
||||
# WHERE "geoapp_country"."name" = 'San Marino';
|
||||
ref = fromstr('MULTIPOLYGON(((12.4 44,12.5 44,12.5 43.9,12.4 43.9,12.4 44)))')
|
||||
self.assertTrue(ref.equals_exact(Country.objects.snap_to_grid(0.1).get(name='San Marino').snap_to_grid, tol))
|
||||
|
||||
# SELECT AsText(ST_SnapToGrid("geoapp_country"."mpoly", 0.05, 0.23)) FROM "geoapp_country"
|
||||
# WHERE "geoapp_country"."name" = 'San Marino';
|
||||
ref = fromstr('MULTIPOLYGON(((12.4 43.93,12.45 43.93,12.5 43.93,12.45 43.93,12.4 43.93)))')
|
||||
self.assertTrue(
|
||||
ref.equals_exact(Country.objects.snap_to_grid(0.05, 0.23).get(name='San Marino').snap_to_grid, tol)
|
||||
)
|
||||
|
||||
# SELECT AsText(ST_SnapToGrid("geoapp_country"."mpoly", 0.5, 0.17, 0.05, 0.23)) FROM "geoapp_country"
|
||||
# WHERE "geoapp_country"."name" = 'San Marino';
|
||||
ref = fromstr(
|
||||
'MULTIPOLYGON(((12.4 43.87,12.45 43.87,12.45 44.1,12.5 44.1,12.5 43.87,12.45 43.87,12.4 43.87)))'
|
||||
)
|
||||
self.assertTrue(
|
||||
ref.equals_exact(
|
||||
Country.objects.snap_to_grid(0.05, 0.23, 0.5, 0.17).get(name='San Marino').snap_to_grid,
|
||||
tol
|
||||
)
|
||||
)
|
||||
|
||||
@skipUnlessDBFeature("has_svg_method")
|
||||
def test_svg(self):
|
||||
"Testing SVG output using GeoQuerySet.svg()."
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
City.objects.svg(precision='foo')
|
||||
# SELECT AsSVG(geoapp_city.point, 0, 8) FROM geoapp_city WHERE name = 'Pueblo';
|
||||
svg1 = 'cx="-104.609252" cy="-38.255001"'
|
||||
# Even though relative, only one point so it's practically the same except for
|
||||
# the 'c' letter prefix on the x,y values.
|
||||
svg2 = svg1.replace('c', '')
|
||||
self.assertEqual(svg1, City.objects.svg().get(name='Pueblo').svg)
|
||||
self.assertEqual(svg2, City.objects.svg(relative=5).get(name='Pueblo').svg)
|
||||
|
||||
@skipUnlessDBFeature("has_transform_method")
|
||||
def test_transform(self):
|
||||
"Testing the transform() GeoQuerySet method."
|
||||
# Pre-transformed points for Houston and Pueblo.
|
||||
htown = fromstr('POINT(1947516.83115183 6322297.06040572)', srid=3084)
|
||||
ptown = fromstr('POINT(992363.390841912 481455.395105533)', srid=2774)
|
||||
prec = 3 # Precision is low due to version variations in PROJ and GDAL.
|
||||
|
||||
# Asserting the result of the transform operation with the values in
|
||||
# the pre-transformed points. Oracle does not have the 3084 SRID.
|
||||
if not oracle:
|
||||
h = City.objects.transform(htown.srid).get(name='Houston')
|
||||
self.assertEqual(3084, h.point.srid)
|
||||
self.assertAlmostEqual(htown.x, h.point.x, prec)
|
||||
self.assertAlmostEqual(htown.y, h.point.y, prec)
|
||||
|
||||
p1 = City.objects.transform(ptown.srid, field_name='point').get(name='Pueblo')
|
||||
p2 = City.objects.transform(srid=ptown.srid).get(name='Pueblo')
|
||||
for p in [p1, p2]:
|
||||
self.assertEqual(2774, p.point.srid)
|
||||
self.assertAlmostEqual(ptown.x, p.point.x, prec)
|
||||
self.assertAlmostEqual(ptown.y, p.point.y, prec)
|
||||
|
||||
@skipUnlessDBFeature("has_translate_method")
|
||||
def test_translate(self):
|
||||
"Testing the `translate` GeoQuerySet method."
|
||||
xfac, yfac = 5, -23
|
||||
qs = Country.objects.translate(xfac, yfac, model_att='translated')
|
||||
for c in qs:
|
||||
for p1, p2 in zip(c.mpoly, c.translated):
|
||||
for r1, r2 in zip(p1, p2):
|
||||
for c1, c2 in zip(r1.coords, r2.coords):
|
||||
# XXX The low precision is for SpatiaLite
|
||||
self.assertAlmostEqual(c1[0] + xfac, c2[0], 5)
|
||||
self.assertAlmostEqual(c1[1] + yfac, c2[1], 5)
|
||||
|
||||
@skipUnlessDBFeature('supports_union_aggr')
|
||||
def test_unionagg(self):
|
||||
"""
|
||||
|
|
|
@ -6,8 +6,6 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
required_db_features = ['gis_enabled']
|
||||
|
|
|
@ -11,11 +11,8 @@ from django.contrib.gis.db.models.functions import Area, Distance
|
|||
from django.contrib.gis.measure import D
|
||||
from django.db import connection
|
||||
from django.db.models.functions import Cast
|
||||
from django.test import (
|
||||
TestCase, ignore_warnings, skipIfDBFeature, skipUnlessDBFeature,
|
||||
)
|
||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||
from django.utils._os import upath
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
|
||||
from ..utils import oracle, postgis, spatialite
|
||||
from .models import City, County, Zipcode
|
||||
|
@ -32,7 +29,7 @@ class GeographyTest(TestCase):
|
|||
@skipIf(spatialite, "SpatiaLite doesn't support distance lookups with Distance objects.")
|
||||
@skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic")
|
||||
def test02_distance_lookup(self):
|
||||
"Testing GeoQuerySet distance lookup support on non-point geography fields."
|
||||
"Testing distance lookup support on non-point geography fields."
|
||||
z = Zipcode.objects.get(code='77002')
|
||||
cities1 = list(City.objects
|
||||
.filter(point__distance_lte=(z.poly, D(mi=500)))
|
||||
|
@ -45,15 +42,6 @@ class GeographyTest(TestCase):
|
|||
for cities in [cities1, cities2]:
|
||||
self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities)
|
||||
|
||||
@skipIf(spatialite, "distance() doesn't support geodetic coordinates on SpatiaLite.")
|
||||
@skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test03_distance_method(self):
|
||||
"Testing GeoQuerySet.distance() support on non-point geography fields."
|
||||
# `GeoQuerySet.distance` is not allowed geometry fields.
|
||||
htown = City.objects.get(name='Houston')
|
||||
Zipcode.objects.distance(htown.point)
|
||||
|
||||
@skipUnless(postgis, "This is a PostGIS-specific test")
|
||||
def test04_invalid_operators_functions(self):
|
||||
"Ensuring exceptions are raised for operators & functions invalid on geography fields."
|
||||
|
@ -101,19 +89,6 @@ class GeographyTest(TestCase):
|
|||
self.assertEqual(name, c.name)
|
||||
self.assertEqual(state, c.state)
|
||||
|
||||
@skipIf(spatialite, "area() doesn't support geodetic coordinates on SpatiaLite.")
|
||||
@skipUnlessDBFeature("has_area_method", "supports_distance_geodetic")
|
||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||
def test06_geography_area(self):
|
||||
"Testing that Area calculations work on geography columns."
|
||||
# SELECT ST_Area(poly) FROM geogapp_zipcode WHERE code='77002';
|
||||
z = Zipcode.objects.area().get(code='77002')
|
||||
# Round to the nearest thousand as possible values (depending on
|
||||
# the database and geolib) include 5439084, 5439100, 5439101.
|
||||
rounded_value = z.area.sq_m
|
||||
rounded_value -= z.area.sq_m % 1000
|
||||
self.assertEqual(rounded_value, 5439000)
|
||||
|
||||
|
||||
@skipUnlessDBFeature("gis_enabled")
|
||||
class GeographyFunctionTests(TestCase):
|
||||
|
|
|
@ -3,9 +3,6 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
|
||||
|
||||
class SimpleModel(models.Model):
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
required_db_features = ['gis_enabled']
|
||||
|
|
|
@ -38,33 +38,6 @@ class RelatedGeoModelTest(TestCase):
|
|||
self.assertEqual(st, c.state)
|
||||
self.assertEqual(Point(lon, lat, srid=c.location.point.srid), c.location.point)
|
||||
|
||||
@skipUnlessDBFeature("has_transform_method")
|
||||
def test03_transform_related(self):
|
||||
"Testing the `transform` GeoQuerySet method on related geographic models."
|
||||
# All the transformations are to state plane coordinate systems using
|
||||
# US Survey Feet (thus a tolerance of 0 implies error w/in 1 survey foot).
|
||||
tol = 0
|
||||
|
||||
def check_pnt(ref, pnt):
|
||||
self.assertAlmostEqual(ref.x, pnt.x, tol)
|
||||
self.assertAlmostEqual(ref.y, pnt.y, tol)
|
||||
self.assertEqual(ref.srid, pnt.srid)
|
||||
|
||||
# Each city transformed to the SRID of their state plane coordinate system.
|
||||
transformed = (('Kecksburg', 2272, 'POINT(1490553.98959621 314792.131023984)'),
|
||||
('Roswell', 2257, 'POINT(481902.189077221 868477.766629735)'),
|
||||
('Aurora', 2276, 'POINT(2269923.2484839 7069381.28722222)'),
|
||||
)
|
||||
|
||||
for name, srid, wkt in transformed:
|
||||
# Doing this implicitly sets `select_related` select the location.
|
||||
# TODO: Fix why this breaks on Oracle.
|
||||
qs = list(City.objects.filter(name=name).transform(srid, field_name='location__point'))
|
||||
check_pnt(GEOSGeometry(wkt, srid), qs[0].location.point)
|
||||
|
||||
# Relations more than one level deep can be queried.
|
||||
self.assertEqual(list(Parcel.objects.transform(srid, field_name='city__location__point')), [])
|
||||
|
||||
@skipUnlessDBFeature("supports_extent_aggr")
|
||||
def test_related_extent_aggregate(self):
|
||||
"Testing the `Extent` aggregate on related geographic models."
|
||||
|
@ -190,13 +163,13 @@ class RelatedGeoModelTest(TestCase):
|
|||
self.assertEqual('P1', qs[0].name)
|
||||
|
||||
def test07_values(self):
|
||||
"Testing values() and values_list() and GeoQuerySets."
|
||||
"Testing values() and values_list()."
|
||||
gqs = Location.objects.all()
|
||||
gvqs = Location.objects.values()
|
||||
gvlqs = Location.objects.values_list()
|
||||
|
||||
# Incrementing through each of the models, dictionaries, and tuples
|
||||
# returned by the different types of GeoQuerySets.
|
||||
# returned by each QuerySet.
|
||||
for m, d, t in zip(gqs, gvqs, gvlqs):
|
||||
# The values should be Geometry objects and not raw strings returned
|
||||
# by the spatial database.
|
||||
|
@ -234,7 +207,7 @@ class RelatedGeoModelTest(TestCase):
|
|||
# TODO: fix on Oracle -- qs2 returns an empty result for an unknown reason
|
||||
@no_oracle
|
||||
def test10_combine(self):
|
||||
"Testing the combination of two GeoQuerySets. See #10807."
|
||||
"Testing the combination of two QuerySets (#10807)."
|
||||
buf1 = City.objects.get(name='Aurora').location.point.buffer(0.1)
|
||||
buf2 = City.objects.get(name='Kecksburg').location.point.buffer(0.1)
|
||||
qs1 = City.objects.filter(location__point__within=buf1)
|
||||
|
|
|
@ -305,19 +305,6 @@ class TestManagerInheritance(TestCase):
|
|||
|
||||
@isolate_apps('managers_regress')
|
||||
class TestManagerDeprecations(TestCase):
|
||||
def test_use_for_related_fields_on_geomanager(self):
|
||||
from django.contrib.gis.db.models import GeoManager
|
||||
|
||||
class MyModel(models.Model):
|
||||
objects = GeoManager()
|
||||
|
||||
# Shouldn't issue any warnings, since GeoManager itself will be
|
||||
# deprecated at the same time as use_for_related_fields, there
|
||||
# is no point annoying users with this deprecation.
|
||||
with warnings.catch_warnings(record=True) as warns:
|
||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
||||
MyModel._base_manager
|
||||
self.assertEqual(len(warns), 0)
|
||||
|
||||
def test_use_for_related_fields_for_base_manager(self):
|
||||
class MyManager(models.Manager):
|
||||
|
|
|
@ -168,12 +168,6 @@ def setup(verbosity, test_labels, parallel):
|
|||
'fields.W901', # CommaSeparatedIntegerField deprecated
|
||||
]
|
||||
|
||||
warnings.filterwarnings(
|
||||
'ignore',
|
||||
'The GeoManager class is deprecated.',
|
||||
RemovedInDjango20Warning
|
||||
)
|
||||
|
||||
# Load all the ALWAYS_INSTALLED_APPS.
|
||||
django.setup()
|
||||
|
||||
|
|
Loading…
Reference in New Issue