Removed GeoManager and GeoQuerySet per deprecation timeline.

This commit is contained in:
Tim Graham 2016-11-07 13:00:40 +01:00
parent e90c745afd
commit a0d166306f
25 changed files with 32 additions and 2194 deletions

View File

@ -1,5 +1,4 @@
import re import re
from functools import partial
from django.contrib.gis.db.models import aggregates from django.contrib.gis.db.models import aggregates
@ -71,17 +70,6 @@ class BaseSpatialFeatures(object):
def supports_isvalid_lookup(self): def supports_isvalid_lookup(self):
return 'isvalid' in self.connection.ops.gis_operators 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? # Is the aggregate supported by the database?
@property @property
def supports_collect_aggr(self): def supports_collect_aggr(self):
@ -99,19 +87,9 @@ class BaseSpatialFeatures(object):
def supports_union_aggr(self): def supports_union_aggr(self):
return aggregates.Union not in self.connection.ops.disallowed_aggregates 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): def __getattr__(self, name):
m = re.match(r'has_(\w*)_function$', name) m = re.match(r'has_(\w*)_function$', name)
if m: if m:
func_name = m.group(1) func_name = m.group(1)
return func_name not in self.connection.ops.unsupported_functions return func_name not in self.connection.ops.unsupported_functions
raise AttributeError raise AttributeError
def has_ops_method(self, method):
return getattr(self.connection.ops, method, False)

View File

@ -55,7 +55,6 @@ class OracleSpatialRefSys(models.Model, SpatialRefSysMixin):
# Optional geometry representing the bounds of this coordinate # Optional geometry representing the bounds of this coordinate
# system. By default, all are NULL in the table. # system. By default, all are NULL in the table.
cs_bounds = models.PolygonField(null=True) cs_bounds = models.PolygonField(null=True)
objects = models.GeoManager()
class Meta: class Meta:
app_label = 'gis' app_label = 'gis'

View File

@ -7,11 +7,10 @@ from django.contrib.gis.db.models.fields import (
MultiLineStringField, MultiPointField, MultiPolygonField, PointField, MultiLineStringField, MultiPointField, MultiPolygonField, PointField,
PolygonField, RasterField, PolygonField, RasterField,
) )
from django.contrib.gis.db.models.manager import GeoManager
__all__ = models_all + aggregates_all __all__ = models_all + aggregates_all
__all__ += [ __all__ += [
'GeometryCollectionField', 'GeometryField', 'LineStringField', 'GeometryCollectionField', 'GeometryField', 'LineStringField',
'MultiLineStringField', 'MultiPointField', 'MultiPolygonField', 'PointField', 'MultiLineStringField', 'MultiPointField', 'MultiPolygonField', 'PointField',
'PolygonField', 'RasterField', 'GeoManager', 'PolygonField', 'RasterField',
] ]

View File

@ -346,27 +346,6 @@ class GeometryField(GeoSelectFormatMixin, BaseSpatialField):
defaults['widget'] = forms.Textarea defaults['widget'] = forms.Textarea
return super(GeometryField, self).formfield(**defaults) 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 # The OpenGIS Geometry Type Fields
class PointField(GeometryField): class PointField(GeometryField):

View File

@ -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)

View File

@ -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))

View File

@ -1,11 +1,9 @@
========================= ==========================
GeoQuerySet API Reference GIS QuerySet API Reference
========================= ==========================
.. currentmodule:: django.contrib.gis.db.models .. currentmodule:: django.contrib.gis.db.models
.. class:: GeoQuerySet(model=None)
.. _spatial-lookups: .. _spatial-lookups:
Spatial Lookups Spatial Lookups
@ -720,593 +718,6 @@ SpatiaLite ``PtDistWithin(poly, geom, 5)``
SpatiaLite support was added. 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 Aggregate Functions
------------------- -------------------

View File

@ -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 determining `when to use geography data type over geometry data type
<http://postgis.net/docs/using_postgis_dbmanagement.html#PostGIS_GeographyVSGeometry>`_. <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 .. rubric:: Footnotes
.. [#fnogc] OpenGIS Consortium, Inc., `Simple Feature Specification For SQL <http://www.opengeospatial.org/standards/sfs>`_. .. [#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). .. [#fnogcsrid] *See id.* at Ch. 2.3.8, p. 39 (Geometry Values and Spatial Reference Systems).

View File

@ -149,11 +149,10 @@ Here's the formal declaration of a ``QuerySet``:
.. note:: .. note::
The ``query`` parameter to :class:`QuerySet` exists so that specialized The ``query`` parameter to :class:`QuerySet` exists so that specialized
query subclasses such as query subclasses can reconstruct internal query state. The value of the
:class:`~django.contrib.gis.db.models.GeoQuerySet` can reconstruct parameter is an opaque representation of that query state and is not
internal query state. The value of the parameter is an opaque part of a public API. To put it simply: if you need to ask, you don't
representation of that query state and is not part of a public API. need to use it.
To put it simply: if you need to ask, you don't need to use it.
.. currentmodule:: django.db.models.query.QuerySet .. currentmodule:: django.db.models.query.QuerySet

View File

@ -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 The :class:`~django.contrib.gis.db.models.Extent3D` aggregate
and ``extent3d()`` ``GeoQuerySet`` method were added as a part of this feature. and ``extent3d()`` ``GeoQuerySet`` method were added as a part of this feature.
The following :class:`~django.contrib.gis.db.models.GeoQuerySet` The ``force_rhr()``, ``reverse_geom()``, and ``geohash()`` ``GeoQuerySet``
methods are new in 1.2: methods are new.
* :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 GEOS interface was updated to use thread-safe C library functions when The GEOS interface was updated to use thread-safe C library functions when
available on the platform. available on the platform.

View File

@ -261,3 +261,5 @@ these features.
ORM, e.g. with ``cursor.execute()``. ORM, e.g. with ``cursor.execute()``.
* ``django.contrib.auth.tests.utils.skipIfCustomUser()`` is removed. * ``django.contrib.auth.tests.utils.skipIfCustomUser()`` is removed.
* The ``GeoManager`` and ``GeoQuerySet`` classes are removed.

View File

@ -8,8 +8,6 @@ from ..utils import gisfield_may_be_null
class NamedModel(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
objects = models.GeoManager()
class Meta: class Meta:
abstract = True abstract = True
required_db_features = ['gis_enabled'] required_db_features = ['gis_enabled']

View File

@ -1,7 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from unittest import skipIf
from django.contrib.gis.db.models.functions import ( from django.contrib.gis.db.models.functions import (
Area, Distance, Length, Perimeter, Transform, 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.contrib.gis.measure import D # alias for Distance
from django.db import connection from django.db import connection
from django.db.models import F, Q from django.db.models import F, Q
from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
from django.utils.deprecation import RemovedInDjango20Warning
from ..utils import no_oracle, oracle, postgis, spatialite from ..utils import no_oracle, oracle, postgis, spatialite
from .models import ( from .models import (
@ -101,136 +98,6 @@ class DistanceTest(TestCase):
else: else:
self.assertListEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist)))) 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") @skipUnlessDBFeature("supports_distances_lookups")
def test_distance_lookups(self): def test_distance_lookups(self):
""" """
@ -347,86 +214,6 @@ class DistanceTest(TestCase):
).order_by('name') ).order_by('name')
self.assertEqual(self.get_names(qs), ['Canberra', 'Hobart', 'Melbourne']) 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'])
''' '''
============================= =============================

View File

@ -6,8 +6,6 @@ from django.utils.encoding import python_2_unicode_compatible
class NamedModel(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
objects = models.GeoManager()
class Meta: class Meta:
abstract = True abstract = True
required_db_features = ['gis_enabled'] required_db_features = ['gis_enabled']

View File

@ -8,9 +8,8 @@ from django.contrib.gis.db.models.functions import (
AsGeoJSON, AsKML, Length, Perimeter, Scale, Translate, AsGeoJSON, AsKML, Length, Perimeter, Scale, Translate,
) )
from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon 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._os import upath
from django.utils.deprecation import RemovedInDjango20Warning
from .models import ( from .models import (
City3D, Interstate2D, Interstate3D, InterstateProj2D, InterstateProj3D, City3D, Interstate2D, Interstate3D, InterstateProj2D, InterstateProj3D,
@ -171,30 +170,6 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
lm.save() lm.save()
self.assertEqual(3, MultiPoint3D.objects.count()) 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") @skipUnlessDBFeature("supports_3d_functions")
def test_union(self): def test_union(self):
""" """
@ -231,84 +206,6 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase):
check_extent3d(extent) check_extent3d(extent)
self.assertIsNone(City3D.objects.none().aggregate(Extent3D('point'))['point__extent3d']) 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") @skipUnlessDBFeature("gis_enabled", "supports_3d_functions")
class Geo3DFunctionsTests(Geo3DLoadingHelper, TestCase): class Geo3DFunctionsTests(Geo3DLoadingHelper, TestCase):

View File

@ -8,8 +8,6 @@ from ..utils import gisfield_may_be_null
class NamedModel(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
objects = models.GeoManager()
class Meta: class Meta:
abstract = True abstract = True
required_db_features = ['gis_enabled'] required_db_features = ['gis_enabled']

View File

@ -19,7 +19,6 @@ from .models import City, Country, CountryWebMercator, State, Track
class GISFunctionsTests(TestCase): class GISFunctionsTests(TestCase):
""" """
Testing functions from django/contrib/gis/db/models/functions.py. 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. Area/Distance/Length/Perimeter are tested in distapp/tests.
Please keep the tests in function's alphabetic order. Please keep the tests in function's alphabetic order.
@ -462,7 +461,6 @@ class GISFunctionsTests(TestCase):
"has_Difference_function", "has_Intersection_function", "has_Difference_function", "has_Intersection_function",
"has_SymDifference_function", "has_Union_function") "has_SymDifference_function", "has_Union_function")
def test_diff_intersection_union(self): def test_diff_intersection_union(self):
"Testing the `difference`, `intersection`, `sym_difference`, and `union` GeoQuerySet methods."
geom = Point(5, 23, srid=4326) geom = Point(5, 23, srid=4326)
qs = Country.objects.all().annotate( qs = Country.objects.all().annotate(
difference=functions.Difference('mpoly', geom), difference=functions.Difference('mpoly', geom),

View File

@ -17,7 +17,7 @@ class GeoRegressionTests(TestCase):
fixtures = ['initial'] fixtures = ['initial']
def test_update(self): def test_update(self):
"Testing GeoQuerySet.update(). See #10411." "Testing QuerySet.update() (#10411)."
pnt = City.objects.get(name='Pueblo').point pnt = City.objects.get(name='Pueblo').point
bak = pnt.clone() bak = pnt.clone()
pnt.y += 0.005 pnt.y += 0.005

View File

@ -1,21 +1,19 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re
import tempfile import tempfile
from django.contrib.gis import gdal 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 ( from django.contrib.gis.geos import (
GeometryCollection, GEOSGeometry, LinearRing, LineString, MultiLineString, GeometryCollection, GEOSGeometry, LinearRing, LineString, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, fromstr, MultiPoint, MultiPolygon, Point, Polygon, fromstr,
) )
from django.core.management import call_command from django.core.management import call_command
from django.db import connection 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 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 ( from .models import (
City, Country, Feature, MinusOneSRID, NonConcreteModel, PennsylvaniaCity, City, Country, Feature, MinusOneSRID, NonConcreteModel, PennsylvaniaCity,
State, Track, State, Track,
@ -155,19 +153,22 @@ class GeoModelTest(TestCase):
self.assertIsInstance(f_4.geom, GeometryCollection) self.assertIsInstance(f_4.geom, GeometryCollection)
self.assertEqual(f_3.geom, f_4.geom[2]) 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") @skipUnlessDBFeature("supports_transform")
def test_inherited_geofields(self): def test_inherited_geofields(self):
"Test GeoQuerySet methods on inherited Geometry fields." "Database functions on inherited Geometry fields."
# Creating a Pennsylvanian city. # Creating a Pennsylvanian city.
PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)') PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)')
# All transformation SQL will need to be performed on the # All transformation SQL will need to be performed on the
# _parent_ table. # _parent_ table.
qs = PennsylvaniaCity.objects.transform(32128) qs = PennsylvaniaCity.objects.annotate(new_point=functions.Transform('point', srid=32128))
self.assertEqual(1, qs.count()) self.assertEqual(1, qs.count())
for pc in qs: for pc in qs:
self.assertEqual(32128, pc.point.srid) self.assertEqual(32128, pc.new_point.srid)
def test_raw_sql_query(self): def test_raw_sql_query(self):
"Testing raw SQL query." "Testing raw SQL query."
@ -412,8 +413,8 @@ class GeoLookupTest(TestCase):
pnt1 = fromstr('POINT (649287.0363174 4177429.4494686)', srid=2847) pnt1 = fromstr('POINT (649287.0363174 4177429.4494686)', srid=2847)
pnt2 = fromstr('POINT(-98.4919715741052 29.4333344025053)', srid=4326) pnt2 = fromstr('POINT(-98.4919715741052 29.4333344025053)', srid=4326)
# Not passing in a geometry as first param should # Not passing in a geometry as first param raises a TypeError when
# raise a type error when initializing the GeoQuerySet # initializing the QuerySet.
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
Country.objects.filter(mpoly__relate=(23, 'foo')) Country.objects.filter(mpoly__relate=(23, 'foo'))
@ -451,66 +452,10 @@ class GeoLookupTest(TestCase):
@skipUnlessDBFeature("gis_enabled") @skipUnlessDBFeature("gis_enabled")
@ignore_warnings(category=RemovedInDjango20Warning)
class GeoQuerySetTest(TestCase): class GeoQuerySetTest(TestCase):
# TODO: GeoQuerySet is removed, organize these test better.
fixtures = ['initial'] 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") @skipUnlessDBFeature("supports_extent_aggr")
def test_extent(self): def test_extent(self):
""" """
@ -536,132 +481,6 @@ class GeoQuerySetTest(TestCase):
extent2 = City.objects.all()[:3].aggregate(Extent('point'))['point__extent'] extent2 = City.objects.all()[:3].aggregate(Extent('point'))['point__extent']
self.assertNotEqual(extent1, extent2) 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): def test_make_line(self):
""" """
Testing the `MakeLine` aggregate. Testing the `MakeLine` aggregate.
@ -689,181 +508,6 @@ class GeoQuerySetTest(TestCase):
"%s != %s" % (ref_line, line) "%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') @skipUnlessDBFeature('supports_union_aggr')
def test_unionagg(self): def test_unionagg(self):
""" """

View File

@ -6,8 +6,6 @@ from django.utils.encoding import python_2_unicode_compatible
class NamedModel(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
objects = models.GeoManager()
class Meta: class Meta:
abstract = True abstract = True
required_db_features = ['gis_enabled'] required_db_features = ['gis_enabled']

View File

@ -11,11 +11,8 @@ from django.contrib.gis.db.models.functions import Area, Distance
from django.contrib.gis.measure import D from django.contrib.gis.measure import D
from django.db import connection from django.db import connection
from django.db.models.functions import Cast from django.db.models.functions import Cast
from django.test import ( from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
TestCase, ignore_warnings, skipIfDBFeature, skipUnlessDBFeature,
)
from django.utils._os import upath from django.utils._os import upath
from django.utils.deprecation import RemovedInDjango20Warning
from ..utils import oracle, postgis, spatialite from ..utils import oracle, postgis, spatialite
from .models import City, County, Zipcode from .models import City, County, Zipcode
@ -32,7 +29,7 @@ class GeographyTest(TestCase):
@skipIf(spatialite, "SpatiaLite doesn't support distance lookups with Distance objects.") @skipIf(spatialite, "SpatiaLite doesn't support distance lookups with Distance objects.")
@skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic") @skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic")
def test02_distance_lookup(self): 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') z = Zipcode.objects.get(code='77002')
cities1 = list(City.objects cities1 = list(City.objects
.filter(point__distance_lte=(z.poly, D(mi=500))) .filter(point__distance_lte=(z.poly, D(mi=500)))
@ -45,15 +42,6 @@ class GeographyTest(TestCase):
for cities in [cities1, cities2]: for cities in [cities1, cities2]:
self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities) 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") @skipUnless(postgis, "This is a PostGIS-specific test")
def test04_invalid_operators_functions(self): def test04_invalid_operators_functions(self):
"Ensuring exceptions are raised for operators & functions invalid on geography fields." "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(name, c.name)
self.assertEqual(state, c.state) 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") @skipUnlessDBFeature("gis_enabled")
class GeographyFunctionTests(TestCase): class GeographyFunctionTests(TestCase):

View File

@ -3,9 +3,6 @@ from django.utils.encoding import python_2_unicode_compatible
class SimpleModel(models.Model): class SimpleModel(models.Model):
objects = models.GeoManager()
class Meta: class Meta:
abstract = True abstract = True
required_db_features = ['gis_enabled'] required_db_features = ['gis_enabled']

View File

@ -38,33 +38,6 @@ class RelatedGeoModelTest(TestCase):
self.assertEqual(st, c.state) self.assertEqual(st, c.state)
self.assertEqual(Point(lon, lat, srid=c.location.point.srid), c.location.point) 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") @skipUnlessDBFeature("supports_extent_aggr")
def test_related_extent_aggregate(self): def test_related_extent_aggregate(self):
"Testing the `Extent` aggregate on related geographic models." "Testing the `Extent` aggregate on related geographic models."
@ -190,13 +163,13 @@ class RelatedGeoModelTest(TestCase):
self.assertEqual('P1', qs[0].name) self.assertEqual('P1', qs[0].name)
def test07_values(self): def test07_values(self):
"Testing values() and values_list() and GeoQuerySets." "Testing values() and values_list()."
gqs = Location.objects.all() gqs = Location.objects.all()
gvqs = Location.objects.values() gvqs = Location.objects.values()
gvlqs = Location.objects.values_list() gvlqs = Location.objects.values_list()
# Incrementing through each of the models, dictionaries, and tuples # 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): for m, d, t in zip(gqs, gvqs, gvlqs):
# The values should be Geometry objects and not raw strings returned # The values should be Geometry objects and not raw strings returned
# by the spatial database. # by the spatial database.
@ -234,7 +207,7 @@ class RelatedGeoModelTest(TestCase):
# TODO: fix on Oracle -- qs2 returns an empty result for an unknown reason # TODO: fix on Oracle -- qs2 returns an empty result for an unknown reason
@no_oracle @no_oracle
def test10_combine(self): 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) buf1 = City.objects.get(name='Aurora').location.point.buffer(0.1)
buf2 = City.objects.get(name='Kecksburg').location.point.buffer(0.1) buf2 = City.objects.get(name='Kecksburg').location.point.buffer(0.1)
qs1 = City.objects.filter(location__point__within=buf1) qs1 = City.objects.filter(location__point__within=buf1)

View File

@ -305,19 +305,6 @@ class TestManagerInheritance(TestCase):
@isolate_apps('managers_regress') @isolate_apps('managers_regress')
class TestManagerDeprecations(TestCase): 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): def test_use_for_related_fields_for_base_manager(self):
class MyManager(models.Manager): class MyManager(models.Manager):

View File

@ -168,12 +168,6 @@ def setup(verbosity, test_labels, parallel):
'fields.W901', # CommaSeparatedIntegerField deprecated 'fields.W901', # CommaSeparatedIntegerField deprecated
] ]
warnings.filterwarnings(
'ignore',
'The GeoManager class is deprecated.',
RemovedInDjango20Warning
)
# Load all the ALWAYS_INSTALLED_APPS. # Load all the ALWAYS_INSTALLED_APPS.
django.setup() django.setup()