Removed GeoManager and GeoQuerySet per deprecation timeline.
This commit is contained in:
parent
e90c745afd
commit
a0d166306f
|
@ -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)
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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',
|
||||||
]
|
]
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.contrib.gis.db.models.query import GeoQuerySet
|
|
||||||
from django.db.models.manager import Manager
|
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
|
|
||||||
|
|
||||||
class GeoManager(Manager.from_queryset(GeoQuerySet)):
|
|
||||||
"Overrides Manager to return Geographic QuerySets."
|
|
||||||
|
|
||||||
# This manager should be used for queries on related fields
|
|
||||||
# so that geometry columns on Oracle and MySQL are selected
|
|
||||||
# properly.
|
|
||||||
use_for_related_fields = True
|
|
||||||
|
|
||||||
# No need to bother users with the use_for_related_fields
|
|
||||||
# deprecation for this manager which is itself deprecated.
|
|
||||||
silence_use_for_related_fields_deprecation = True
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
warnings.warn(
|
|
||||||
"The GeoManager class is deprecated. Simply use a normal manager "
|
|
||||||
"once you have replaced all calls to GeoQuerySet methods by annotations.",
|
|
||||||
RemovedInDjango20Warning, stacklevel=2
|
|
||||||
)
|
|
||||||
super(GeoManager, self).__init__(*args, **kwargs)
|
|
|
@ -1,727 +0,0 @@
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.contrib.gis.db.models.fields import (
|
|
||||||
GeometryField, LineStringField, PointField, get_srid_info,
|
|
||||||
)
|
|
||||||
from django.contrib.gis.db.models.lookups import GISLookup
|
|
||||||
from django.contrib.gis.db.models.sql import (
|
|
||||||
AreaField, DistanceField, GeomField, GMLField,
|
|
||||||
)
|
|
||||||
from django.contrib.gis.geometry.backend import Geometry
|
|
||||||
from django.contrib.gis.measure import Area, Distance
|
|
||||||
from django.db import connections
|
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
|
||||||
from django.db.models.expressions import RawSQL
|
|
||||||
from django.db.models.fields import Field
|
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
from django.utils import six
|
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
|
|
||||||
|
|
||||||
class GeoQuerySet(QuerySet):
|
|
||||||
"The Geographic QuerySet."
|
|
||||||
|
|
||||||
# ### GeoQuerySet Methods ###
|
|
||||||
def area(self, tolerance=0.05, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the area of the geographic field in an `area` attribute on
|
|
||||||
each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
# Performing setup here rather than in `_spatial_attribute` so that
|
|
||||||
# we can get the units for `AreaField`.
|
|
||||||
procedure_args, geo_field = self._spatial_setup(
|
|
||||||
'area', field_name=kwargs.get('field_name'))
|
|
||||||
s = {'procedure_args': procedure_args,
|
|
||||||
'geo_field': geo_field,
|
|
||||||
'setup': False,
|
|
||||||
}
|
|
||||||
connection = connections[self.db]
|
|
||||||
backend = connection.ops
|
|
||||||
if backend.oracle:
|
|
||||||
s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
|
|
||||||
s['procedure_args']['tolerance'] = tolerance
|
|
||||||
s['select_field'] = AreaField('sq_m') # Oracle returns area in units of meters.
|
|
||||||
elif backend.postgis or backend.spatialite:
|
|
||||||
if backend.geography:
|
|
||||||
# Geography fields support area calculation, returns square meters.
|
|
||||||
s['select_field'] = AreaField('sq_m')
|
|
||||||
elif not geo_field.geodetic(connection):
|
|
||||||
# Getting the area units of the geographic field.
|
|
||||||
s['select_field'] = AreaField(Area.unit_attname(geo_field.units_name(connection)))
|
|
||||||
else:
|
|
||||||
# TODO: Do we want to support raw number areas for geodetic fields?
|
|
||||||
raise Exception('Area on geodetic coordinate systems not supported.')
|
|
||||||
return self._spatial_attribute('area', s, **kwargs)
|
|
||||||
|
|
||||||
def centroid(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the centroid of the geographic field in a `centroid`
|
|
||||||
attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geom_attribute('centroid', **kwargs)
|
|
||||||
|
|
||||||
def difference(self, geom, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the spatial difference of the geographic field in a `difference`
|
|
||||||
attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geomset_attribute('difference', geom, **kwargs)
|
|
||||||
|
|
||||||
def distance(self, geom, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the distance from the given geographic field name to the
|
|
||||||
given geometry in a `distance` attribute on each element of the
|
|
||||||
GeoQuerySet.
|
|
||||||
|
|
||||||
Keyword Arguments:
|
|
||||||
`spheroid` => If the geometry field is geodetic and PostGIS is
|
|
||||||
the spatial database, then the more accurate
|
|
||||||
spheroid calculation will be used instead of the
|
|
||||||
quicker sphere calculation.
|
|
||||||
|
|
||||||
`tolerance` => Used only for Oracle. The tolerance is
|
|
||||||
in meters -- a default of 5 centimeters (0.05)
|
|
||||||
is used.
|
|
||||||
"""
|
|
||||||
return self._distance_attribute('distance', geom, **kwargs)
|
|
||||||
|
|
||||||
def envelope(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns a Geometry representing the bounding box of the
|
|
||||||
Geometry field in an `envelope` attribute on each element of
|
|
||||||
the GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geom_attribute('envelope', **kwargs)
|
|
||||||
|
|
||||||
def force_rhr(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns a modified version of the Polygon/MultiPolygon in which
|
|
||||||
all of the vertices follow the Right-Hand-Rule. By default,
|
|
||||||
this is attached as the `force_rhr` attribute on each element
|
|
||||||
of the GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geom_attribute('force_rhr', **kwargs)
|
|
||||||
|
|
||||||
def geojson(self, precision=8, crs=False, bbox=False, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns a GeoJSON representation of the geometry field in a `geojson`
|
|
||||||
attribute on each element of the GeoQuerySet.
|
|
||||||
|
|
||||||
The `crs` and `bbox` keywords may be set to True if the user wants
|
|
||||||
the coordinate reference system and the bounding box to be included
|
|
||||||
in the GeoJSON representation of the geometry.
|
|
||||||
"""
|
|
||||||
backend = connections[self.db].ops
|
|
||||||
if not backend.geojson:
|
|
||||||
raise NotImplementedError('Only PostGIS and SpatiaLite support GeoJSON serialization.')
|
|
||||||
|
|
||||||
if not isinstance(precision, six.integer_types):
|
|
||||||
raise TypeError('Precision keyword must be set with an integer.')
|
|
||||||
|
|
||||||
options = 0
|
|
||||||
if crs and bbox:
|
|
||||||
options = 3
|
|
||||||
elif bbox:
|
|
||||||
options = 1
|
|
||||||
elif crs:
|
|
||||||
options = 2
|
|
||||||
s = {'desc': 'GeoJSON',
|
|
||||||
'procedure_args': {'precision': precision, 'options': options},
|
|
||||||
'procedure_fmt': '%(geo_col)s,%(precision)s,%(options)s',
|
|
||||||
}
|
|
||||||
return self._spatial_attribute('geojson', s, **kwargs)
|
|
||||||
|
|
||||||
def geohash(self, precision=20, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns a GeoHash representation of the given field in a `geohash`
|
|
||||||
attribute on each element of the GeoQuerySet.
|
|
||||||
|
|
||||||
The `precision` keyword may be used to custom the number of
|
|
||||||
_characters_ used in the output GeoHash, the default is 20.
|
|
||||||
"""
|
|
||||||
s = {'desc': 'GeoHash',
|
|
||||||
'procedure_args': {'precision': precision},
|
|
||||||
'procedure_fmt': '%(geo_col)s,%(precision)s',
|
|
||||||
}
|
|
||||||
return self._spatial_attribute('geohash', s, **kwargs)
|
|
||||||
|
|
||||||
def gml(self, precision=8, version=2, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns GML representation of the given field in a `gml` attribute
|
|
||||||
on each element of the GeoQuerySet.
|
|
||||||
"""
|
|
||||||
backend = connections[self.db].ops
|
|
||||||
s = {'desc': 'GML', 'procedure_args': {'precision': precision}}
|
|
||||||
if backend.postgis:
|
|
||||||
s['procedure_fmt'] = '%(version)s,%(geo_col)s,%(precision)s'
|
|
||||||
s['procedure_args'] = {'precision': precision, 'version': version}
|
|
||||||
if backend.oracle:
|
|
||||||
s['select_field'] = GMLField()
|
|
||||||
|
|
||||||
return self._spatial_attribute('gml', s, **kwargs)
|
|
||||||
|
|
||||||
def intersection(self, geom, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the spatial intersection of the Geometry field in
|
|
||||||
an `intersection` attribute on each element of this
|
|
||||||
GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geomset_attribute('intersection', geom, **kwargs)
|
|
||||||
|
|
||||||
def kml(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns KML representation of the geometry field in a `kml`
|
|
||||||
attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
s = {'desc': 'KML',
|
|
||||||
'procedure_fmt': '%(geo_col)s,%(precision)s',
|
|
||||||
'procedure_args': {'precision': kwargs.pop('precision', 8)},
|
|
||||||
}
|
|
||||||
return self._spatial_attribute('kml', s, **kwargs)
|
|
||||||
|
|
||||||
def length(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the length of the geometry field as a `Distance` object
|
|
||||||
stored in a `length` attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._distance_attribute('length', None, **kwargs)
|
|
||||||
|
|
||||||
def mem_size(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the memory size (number of bytes) that the geometry field takes
|
|
||||||
in a `mem_size` attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._spatial_attribute('mem_size', {}, **kwargs)
|
|
||||||
|
|
||||||
def num_geom(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the number of geometries if the field is a
|
|
||||||
GeometryCollection or Multi* Field in a `num_geom`
|
|
||||||
attribute on each element of this GeoQuerySet; otherwise
|
|
||||||
the sets with None.
|
|
||||||
"""
|
|
||||||
return self._spatial_attribute('num_geom', {}, **kwargs)
|
|
||||||
|
|
||||||
def num_points(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the number of points in the first linestring in the
|
|
||||||
Geometry field in a `num_points` attribute on each element of
|
|
||||||
this GeoQuerySet; otherwise sets with None.
|
|
||||||
"""
|
|
||||||
return self._spatial_attribute('num_points', {}, **kwargs)
|
|
||||||
|
|
||||||
def perimeter(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the perimeter of the geometry field as a `Distance` object
|
|
||||||
stored in a `perimeter` attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._distance_attribute('perimeter', None, **kwargs)
|
|
||||||
|
|
||||||
def point_on_surface(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns a Point geometry guaranteed to lie on the surface of the
|
|
||||||
Geometry field in a `point_on_surface` attribute on each element
|
|
||||||
of this GeoQuerySet; otherwise sets with None.
|
|
||||||
"""
|
|
||||||
return self._geom_attribute('point_on_surface', **kwargs)
|
|
||||||
|
|
||||||
def reverse_geom(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Reverses the coordinate order of the geometry, and attaches as a
|
|
||||||
`reverse` attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
s = {'select_field': GeomField()}
|
|
||||||
kwargs.setdefault('model_att', 'reverse_geom')
|
|
||||||
if connections[self.db].ops.oracle:
|
|
||||||
s['geo_field_type'] = LineStringField
|
|
||||||
return self._spatial_attribute('reverse', s, **kwargs)
|
|
||||||
|
|
||||||
def scale(self, x, y, z=0.0, **kwargs):
|
|
||||||
"""
|
|
||||||
Scales the geometry to a new size by multiplying the ordinates
|
|
||||||
with the given x,y,z scale factors.
|
|
||||||
"""
|
|
||||||
if connections[self.db].ops.spatialite:
|
|
||||||
if z != 0.0:
|
|
||||||
raise NotImplementedError('SpatiaLite does not support 3D scaling.')
|
|
||||||
s = {'procedure_fmt': '%(geo_col)s,%(x)s,%(y)s',
|
|
||||||
'procedure_args': {'x': x, 'y': y},
|
|
||||||
'select_field': GeomField(),
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
s = {'procedure_fmt': '%(geo_col)s,%(x)s,%(y)s,%(z)s',
|
|
||||||
'procedure_args': {'x': x, 'y': y, 'z': z},
|
|
||||||
'select_field': GeomField(),
|
|
||||||
}
|
|
||||||
return self._spatial_attribute('scale', s, **kwargs)
|
|
||||||
|
|
||||||
def snap_to_grid(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Snap all points of the input geometry to the grid. How the
|
|
||||||
geometry is snapped to the grid depends on how many arguments
|
|
||||||
were given:
|
|
||||||
- 1 argument : A single size to snap both the X and Y grids to.
|
|
||||||
- 2 arguments: X and Y sizes to snap the grid to.
|
|
||||||
- 4 arguments: X, Y sizes and the X, Y origins.
|
|
||||||
"""
|
|
||||||
if False in [isinstance(arg, (float,) + six.integer_types) for arg in args]:
|
|
||||||
raise TypeError('Size argument(s) for the grid must be a float or integer values.')
|
|
||||||
|
|
||||||
nargs = len(args)
|
|
||||||
if nargs == 1:
|
|
||||||
size = args[0]
|
|
||||||
procedure_fmt = '%(geo_col)s,%(size)s'
|
|
||||||
procedure_args = {'size': size}
|
|
||||||
elif nargs == 2:
|
|
||||||
xsize, ysize = args
|
|
||||||
procedure_fmt = '%(geo_col)s,%(xsize)s,%(ysize)s'
|
|
||||||
procedure_args = {'xsize': xsize, 'ysize': ysize}
|
|
||||||
elif nargs == 4:
|
|
||||||
xsize, ysize, xorigin, yorigin = args
|
|
||||||
procedure_fmt = '%(geo_col)s,%(xorigin)s,%(yorigin)s,%(xsize)s,%(ysize)s'
|
|
||||||
procedure_args = {'xsize': xsize, 'ysize': ysize,
|
|
||||||
'xorigin': xorigin, 'yorigin': yorigin}
|
|
||||||
else:
|
|
||||||
raise ValueError('Must provide 1, 2, or 4 arguments to `snap_to_grid`.')
|
|
||||||
|
|
||||||
s = {'procedure_fmt': procedure_fmt,
|
|
||||||
'procedure_args': procedure_args,
|
|
||||||
'select_field': GeomField(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return self._spatial_attribute('snap_to_grid', s, **kwargs)
|
|
||||||
|
|
||||||
def svg(self, relative=False, precision=8, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns SVG representation of the geographic field in a `svg`
|
|
||||||
attribute on each element of this GeoQuerySet.
|
|
||||||
|
|
||||||
Keyword Arguments:
|
|
||||||
`relative` => If set to True, this will evaluate the path in
|
|
||||||
terms of relative moves (rather than absolute).
|
|
||||||
|
|
||||||
`precision` => May be used to set the maximum number of decimal
|
|
||||||
digits used in output (defaults to 8).
|
|
||||||
"""
|
|
||||||
relative = int(bool(relative))
|
|
||||||
if not isinstance(precision, six.integer_types):
|
|
||||||
raise TypeError('SVG precision keyword argument must be an integer.')
|
|
||||||
s = {
|
|
||||||
'desc': 'SVG',
|
|
||||||
'procedure_fmt': '%(geo_col)s,%(rel)s,%(precision)s',
|
|
||||||
'procedure_args': {
|
|
||||||
'rel': relative,
|
|
||||||
'precision': precision,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return self._spatial_attribute('svg', s, **kwargs)
|
|
||||||
|
|
||||||
def sym_difference(self, geom, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the symmetric difference of the geographic field in a
|
|
||||||
`sym_difference` attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geomset_attribute('sym_difference', geom, **kwargs)
|
|
||||||
|
|
||||||
def translate(self, x, y, z=0.0, **kwargs):
|
|
||||||
"""
|
|
||||||
Translates the geometry to a new location using the given numeric
|
|
||||||
parameters as offsets.
|
|
||||||
"""
|
|
||||||
if connections[self.db].ops.spatialite:
|
|
||||||
if z != 0.0:
|
|
||||||
raise NotImplementedError('SpatiaLite does not support 3D translation.')
|
|
||||||
s = {'procedure_fmt': '%(geo_col)s,%(x)s,%(y)s',
|
|
||||||
'procedure_args': {'x': x, 'y': y},
|
|
||||||
'select_field': GeomField(),
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
s = {'procedure_fmt': '%(geo_col)s,%(x)s,%(y)s,%(z)s',
|
|
||||||
'procedure_args': {'x': x, 'y': y, 'z': z},
|
|
||||||
'select_field': GeomField(),
|
|
||||||
}
|
|
||||||
return self._spatial_attribute('translate', s, **kwargs)
|
|
||||||
|
|
||||||
def transform(self, srid=4326, **kwargs):
|
|
||||||
"""
|
|
||||||
Transforms the given geometry field to the given SRID. If no SRID is
|
|
||||||
provided, the transformation will default to using 4326 (WGS84).
|
|
||||||
"""
|
|
||||||
if not isinstance(srid, six.integer_types):
|
|
||||||
raise TypeError('An integer SRID must be provided.')
|
|
||||||
field_name = kwargs.get('field_name')
|
|
||||||
self._spatial_setup('transform', field_name=field_name)
|
|
||||||
self.query.add_context('transformed_srid', srid)
|
|
||||||
return self._clone()
|
|
||||||
|
|
||||||
def union(self, geom, **kwargs):
|
|
||||||
"""
|
|
||||||
Returns the union of the geographic field with the given
|
|
||||||
Geometry in a `union` attribute on each element of this GeoQuerySet.
|
|
||||||
"""
|
|
||||||
return self._geomset_attribute('union', geom, **kwargs)
|
|
||||||
|
|
||||||
# ### Private API -- Abstracted DRY routines. ###
|
|
||||||
def _spatial_setup(self, att, desc=None, field_name=None, geo_field_type=None):
|
|
||||||
"""
|
|
||||||
Performs set up for executing the spatial function.
|
|
||||||
"""
|
|
||||||
# Does the spatial backend support this?
|
|
||||||
connection = connections[self.db]
|
|
||||||
func = getattr(connection.ops, att, False)
|
|
||||||
if desc is None:
|
|
||||||
desc = att
|
|
||||||
if not func:
|
|
||||||
raise NotImplementedError('%s stored procedure not available on '
|
|
||||||
'the %s backend.' %
|
|
||||||
(desc, connection.ops.name))
|
|
||||||
|
|
||||||
# Initializing the procedure arguments.
|
|
||||||
procedure_args = {'function': func}
|
|
||||||
|
|
||||||
# Is there a geographic field in the model to perform this
|
|
||||||
# operation on?
|
|
||||||
geo_field = self._geo_field(field_name)
|
|
||||||
if not geo_field:
|
|
||||||
raise TypeError('%s output only available on GeometryFields.' % func)
|
|
||||||
|
|
||||||
# If the `geo_field_type` keyword was used, then enforce that
|
|
||||||
# type limitation.
|
|
||||||
if geo_field_type is not None and not isinstance(geo_field, geo_field_type):
|
|
||||||
raise TypeError('"%s" stored procedures may only be called on %ss.' % (func, geo_field_type.__name__))
|
|
||||||
|
|
||||||
# Setting the procedure args.
|
|
||||||
procedure_args['geo_col'] = self._geocol_select(geo_field, field_name)
|
|
||||||
|
|
||||||
return procedure_args, geo_field
|
|
||||||
|
|
||||||
def _spatial_attribute(self, att, settings, field_name=None, model_att=None):
|
|
||||||
"""
|
|
||||||
DRY routine for calling a spatial stored procedure on a geometry column
|
|
||||||
and attaching its output as an attribute of the model.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
att:
|
|
||||||
The name of the spatial attribute that holds the spatial
|
|
||||||
SQL function to call.
|
|
||||||
|
|
||||||
settings:
|
|
||||||
Dictionary of internal settings to customize for the spatial procedure.
|
|
||||||
|
|
||||||
Public Keyword Arguments:
|
|
||||||
|
|
||||||
field_name:
|
|
||||||
The name of the geographic field to call the spatial
|
|
||||||
function on. May also be a lookup to a geometry field
|
|
||||||
as part of a foreign key relation.
|
|
||||||
|
|
||||||
model_att:
|
|
||||||
The name of the model attribute to attach the output of
|
|
||||||
the spatial function to.
|
|
||||||
"""
|
|
||||||
warnings.warn(
|
|
||||||
"The %s GeoQuerySet method is deprecated. See GeoDjango Functions "
|
|
||||||
"documentation to find the expression-based replacement." % att,
|
|
||||||
RemovedInDjango20Warning, stacklevel=2
|
|
||||||
)
|
|
||||||
# Default settings.
|
|
||||||
settings.setdefault('desc', None)
|
|
||||||
settings.setdefault('geom_args', ())
|
|
||||||
settings.setdefault('geom_field', None)
|
|
||||||
settings.setdefault('procedure_args', {})
|
|
||||||
settings.setdefault('procedure_fmt', '%(geo_col)s')
|
|
||||||
settings.setdefault('select_params', [])
|
|
||||||
|
|
||||||
connection = connections[self.db]
|
|
||||||
|
|
||||||
# Performing setup for the spatial column, unless told not to.
|
|
||||||
if settings.get('setup', True):
|
|
||||||
default_args, geo_field = self._spatial_setup(
|
|
||||||
att, desc=settings['desc'], field_name=field_name,
|
|
||||||
geo_field_type=settings.get('geo_field_type'))
|
|
||||||
for k, v in six.iteritems(default_args):
|
|
||||||
settings['procedure_args'].setdefault(k, v)
|
|
||||||
else:
|
|
||||||
geo_field = settings['geo_field']
|
|
||||||
|
|
||||||
# The attribute to attach to the model.
|
|
||||||
if not isinstance(model_att, six.string_types):
|
|
||||||
model_att = att
|
|
||||||
|
|
||||||
# Special handling for any argument that is a geometry.
|
|
||||||
for name in settings['geom_args']:
|
|
||||||
# Using the field's get_placeholder() routine to get any needed
|
|
||||||
# transformation SQL.
|
|
||||||
geom = geo_field.get_prep_value(settings['procedure_args'][name])
|
|
||||||
params = geo_field._get_db_prep_lookup('contains', geom, connection=connection)
|
|
||||||
geom_placeholder = geo_field.get_placeholder(geom, None, connection)
|
|
||||||
|
|
||||||
# Replacing the procedure format with that of any needed
|
|
||||||
# transformation SQL.
|
|
||||||
old_fmt = '%%(%s)s' % name
|
|
||||||
new_fmt = geom_placeholder % '%%s'
|
|
||||||
settings['procedure_fmt'] = settings['procedure_fmt'].replace(old_fmt, new_fmt)
|
|
||||||
settings['select_params'].extend(params)
|
|
||||||
|
|
||||||
# Getting the format for the stored procedure.
|
|
||||||
fmt = '%%(function)s(%s)' % settings['procedure_fmt']
|
|
||||||
|
|
||||||
# If the result of this function needs to be converted.
|
|
||||||
if settings.get('select_field'):
|
|
||||||
select_field = settings['select_field']
|
|
||||||
if connection.ops.oracle:
|
|
||||||
select_field.empty_strings_allowed = False
|
|
||||||
else:
|
|
||||||
select_field = Field()
|
|
||||||
|
|
||||||
# Finally, setting the extra selection attribute with
|
|
||||||
# the format string expanded with the stored procedure
|
|
||||||
# arguments.
|
|
||||||
self.query.add_annotation(
|
|
||||||
RawSQL(fmt % settings['procedure_args'], settings['select_params'], select_field),
|
|
||||||
model_att)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs):
|
|
||||||
"""
|
|
||||||
DRY routine for GeoQuerySet distance attribute routines.
|
|
||||||
"""
|
|
||||||
# Setting up the distance procedure arguments.
|
|
||||||
procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name'))
|
|
||||||
|
|
||||||
# If geodetic defaulting distance attribute to meters (Oracle and
|
|
||||||
# PostGIS spherical distances return meters). Otherwise, use the
|
|
||||||
# units of the geometry field.
|
|
||||||
connection = connections[self.db]
|
|
||||||
geodetic = geo_field.geodetic(connection)
|
|
||||||
geography = geo_field.geography
|
|
||||||
|
|
||||||
if geodetic:
|
|
||||||
dist_att = 'm'
|
|
||||||
else:
|
|
||||||
dist_att = Distance.unit_attname(geo_field.units_name(connection))
|
|
||||||
|
|
||||||
# Shortcut booleans for what distance function we're using and
|
|
||||||
# whether the geometry field is 3D.
|
|
||||||
distance = func == 'distance'
|
|
||||||
length = func == 'length'
|
|
||||||
perimeter = func == 'perimeter'
|
|
||||||
if not (distance or length or perimeter):
|
|
||||||
raise ValueError('Unknown distance function: %s' % func)
|
|
||||||
geom_3d = geo_field.dim == 3
|
|
||||||
|
|
||||||
# The field's _get_db_prep_lookup() is used to get any
|
|
||||||
# extra distance parameters. Here we set up the
|
|
||||||
# parameters that will be passed in to field's function.
|
|
||||||
lookup_params = [geom or 'POINT (0 0)', 0]
|
|
||||||
|
|
||||||
# Getting the spatial backend operations.
|
|
||||||
backend = connection.ops
|
|
||||||
|
|
||||||
# If the spheroid calculation is desired, either by the `spheroid`
|
|
||||||
# keyword or when calculating the length of geodetic field, make
|
|
||||||
# sure the 'spheroid' distance setting string is passed in so we
|
|
||||||
# get the correct spatial stored procedure.
|
|
||||||
if spheroid or (backend.postgis and geodetic and
|
|
||||||
(not geography) and length):
|
|
||||||
lookup_params.append('spheroid')
|
|
||||||
lookup_params = geo_field.get_prep_value(lookup_params)
|
|
||||||
params = geo_field._get_db_prep_lookup('distance_lte', lookup_params, connection=connection)
|
|
||||||
|
|
||||||
# The `geom_args` flag is set to true if a geometry parameter was
|
|
||||||
# passed in.
|
|
||||||
geom_args = bool(geom)
|
|
||||||
|
|
||||||
if backend.oracle:
|
|
||||||
if distance:
|
|
||||||
procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s'
|
|
||||||
elif length or perimeter:
|
|
||||||
procedure_fmt = '%(geo_col)s,%(tolerance)s'
|
|
||||||
procedure_args['tolerance'] = tolerance
|
|
||||||
else:
|
|
||||||
# Getting whether this field is in units of degrees since the field may have
|
|
||||||
# been transformed via the `transform` GeoQuerySet method.
|
|
||||||
srid = self.query.get_context('transformed_srid')
|
|
||||||
if srid:
|
|
||||||
u, unit_name, s = get_srid_info(srid, connection)
|
|
||||||
geodetic = unit_name.lower() in geo_field.geodetic_units
|
|
||||||
|
|
||||||
if geodetic and (not connection.features.supports_distance_geodetic or connection.ops.spatialite):
|
|
||||||
raise ValueError(
|
|
||||||
'This database does not support linear distance '
|
|
||||||
'calculations on geodetic coordinate systems.'
|
|
||||||
)
|
|
||||||
|
|
||||||
if distance:
|
|
||||||
if srid:
|
|
||||||
# Setting the `geom_args` flag to false because we want to handle
|
|
||||||
# transformation SQL here, rather than the way done by default
|
|
||||||
# (which will transform to the original SRID of the field rather
|
|
||||||
# than to what was transformed to).
|
|
||||||
geom_args = False
|
|
||||||
procedure_fmt = '%s(%%(geo_col)s, %s)' % (backend.transform, srid)
|
|
||||||
if geom.srid is None or geom.srid == srid:
|
|
||||||
# If the geom parameter srid is None, it is assumed the coordinates
|
|
||||||
# are in the transformed units. A placeholder is used for the
|
|
||||||
# geometry parameter. `GeomFromText` constructor is also needed
|
|
||||||
# to wrap geom placeholder for SpatiaLite.
|
|
||||||
if backend.spatialite:
|
|
||||||
procedure_fmt += ', %s(%%%%s, %s)' % (backend.from_text, srid)
|
|
||||||
else:
|
|
||||||
procedure_fmt += ', %%s'
|
|
||||||
else:
|
|
||||||
# We need to transform the geom to the srid specified in `transform()`,
|
|
||||||
# so wrapping the geometry placeholder in transformation SQL.
|
|
||||||
# SpatiaLite also needs geometry placeholder wrapped in `GeomFromText`
|
|
||||||
# constructor.
|
|
||||||
if backend.spatialite:
|
|
||||||
procedure_fmt += (', %s(%s(%%%%s, %s), %s)' % (
|
|
||||||
backend.transform, backend.from_text,
|
|
||||||
geom.srid, srid))
|
|
||||||
else:
|
|
||||||
procedure_fmt += ', %s(%%%%s, %s)' % (backend.transform, srid)
|
|
||||||
else:
|
|
||||||
# `transform()` was not used on this GeoQuerySet.
|
|
||||||
procedure_fmt = '%(geo_col)s,%(geom)s'
|
|
||||||
|
|
||||||
if not geography and geodetic:
|
|
||||||
# Spherical distance calculation is needed (because the geographic
|
|
||||||
# field is geodetic). However, the PostGIS ST_distance_sphere/spheroid()
|
|
||||||
# procedures may only do queries from point columns to point geometries
|
|
||||||
# some error checking is required.
|
|
||||||
if not backend.geography:
|
|
||||||
if not isinstance(geo_field, PointField):
|
|
||||||
raise ValueError('Spherical distance calculation only supported on PointFields.')
|
|
||||||
if not str(Geometry(six.memoryview(params[0].ewkb)).geom_type) == 'Point':
|
|
||||||
raise ValueError(
|
|
||||||
'Spherical distance calculation only supported with '
|
|
||||||
'Point Geometry parameters'
|
|
||||||
)
|
|
||||||
# The `function` procedure argument needs to be set differently for
|
|
||||||
# geodetic distance calculations.
|
|
||||||
if spheroid:
|
|
||||||
# Call to distance_spheroid() requires spheroid param as well.
|
|
||||||
procedure_fmt += ",'%(spheroid)s'"
|
|
||||||
procedure_args.update({'function': backend.distance_spheroid, 'spheroid': params[1]})
|
|
||||||
else:
|
|
||||||
procedure_args.update({'function': backend.distance_sphere})
|
|
||||||
elif length or perimeter:
|
|
||||||
procedure_fmt = '%(geo_col)s'
|
|
||||||
if not geography and geodetic and length:
|
|
||||||
# There's no `length_sphere`, and `length_spheroid` also
|
|
||||||
# works on 3D geometries.
|
|
||||||
procedure_fmt += ",'%(spheroid)s'"
|
|
||||||
procedure_args.update({'function': backend.length_spheroid, 'spheroid': params[1]})
|
|
||||||
elif geom_3d and connection.features.supports_3d_functions:
|
|
||||||
# Use 3D variants of perimeter and length routines on supported backends.
|
|
||||||
if perimeter:
|
|
||||||
procedure_args.update({'function': backend.perimeter3d})
|
|
||||||
elif length:
|
|
||||||
procedure_args.update({'function': backend.length3d})
|
|
||||||
|
|
||||||
# Setting up the settings for `_spatial_attribute`.
|
|
||||||
s = {'select_field': DistanceField(dist_att),
|
|
||||||
'setup': False,
|
|
||||||
'geo_field': geo_field,
|
|
||||||
'procedure_args': procedure_args,
|
|
||||||
'procedure_fmt': procedure_fmt,
|
|
||||||
}
|
|
||||||
if geom_args:
|
|
||||||
s['geom_args'] = ('geom',)
|
|
||||||
s['procedure_args']['geom'] = geom
|
|
||||||
elif geom:
|
|
||||||
# The geometry is passed in as a parameter because we handled
|
|
||||||
# transformation conditions in this routine.
|
|
||||||
s['select_params'] = [backend.Adapter(geom)]
|
|
||||||
return self._spatial_attribute(func, s, **kwargs)
|
|
||||||
|
|
||||||
def _geom_attribute(self, func, tolerance=0.05, **kwargs):
|
|
||||||
"""
|
|
||||||
DRY routine for setting up a GeoQuerySet method that attaches a
|
|
||||||
Geometry attribute (e.g., `centroid`, `point_on_surface`).
|
|
||||||
"""
|
|
||||||
s = {'select_field': GeomField()}
|
|
||||||
if connections[self.db].ops.oracle:
|
|
||||||
s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s'
|
|
||||||
s['procedure_args'] = {'tolerance': tolerance}
|
|
||||||
return self._spatial_attribute(func, s, **kwargs)
|
|
||||||
|
|
||||||
def _geomset_attribute(self, func, geom, tolerance=0.05, **kwargs):
|
|
||||||
"""
|
|
||||||
DRY routine for setting up a GeoQuerySet method that attaches a
|
|
||||||
Geometry attribute and takes a Geoemtry parameter. This is used
|
|
||||||
for geometry set-like operations (e.g., intersection, difference,
|
|
||||||
union, sym_difference).
|
|
||||||
"""
|
|
||||||
s = {
|
|
||||||
'geom_args': ('geom',),
|
|
||||||
'select_field': GeomField(),
|
|
||||||
'procedure_fmt': '%(geo_col)s,%(geom)s',
|
|
||||||
'procedure_args': {'geom': geom},
|
|
||||||
}
|
|
||||||
if connections[self.db].ops.oracle:
|
|
||||||
s['procedure_fmt'] += ',%(tolerance)s'
|
|
||||||
s['procedure_args']['tolerance'] = tolerance
|
|
||||||
return self._spatial_attribute(func, s, **kwargs)
|
|
||||||
|
|
||||||
def _geocol_select(self, geo_field, field_name):
|
|
||||||
"""
|
|
||||||
Helper routine for constructing the SQL to select the geographic
|
|
||||||
column. Takes into account if the geographic field is in a
|
|
||||||
ForeignKey relation to the current model.
|
|
||||||
"""
|
|
||||||
compiler = self.query.get_compiler(self.db)
|
|
||||||
opts = self.model._meta
|
|
||||||
if geo_field not in opts.fields:
|
|
||||||
# Is this operation going to be on a related geographic field?
|
|
||||||
# If so, it'll have to be added to the select related information
|
|
||||||
# (e.g., if 'location__point' was given as the field name, then
|
|
||||||
# chop the non-relational field and add select_related('location')).
|
|
||||||
# Note: the operation really is defined as "must add select related!"
|
|
||||||
self.query.add_select_related([field_name.rsplit(LOOKUP_SEP, 1)[0]])
|
|
||||||
# Call pre_sql_setup() so that compiler.select gets populated.
|
|
||||||
compiler.pre_sql_setup()
|
|
||||||
for col, _, _ in compiler.select:
|
|
||||||
if col.output_field == geo_field:
|
|
||||||
return col.as_sql(compiler, compiler.connection)[0]
|
|
||||||
raise ValueError("%r not in compiler's related_select_cols" % geo_field)
|
|
||||||
elif geo_field not in opts.local_fields:
|
|
||||||
# This geographic field is inherited from another model, so we have to
|
|
||||||
# use the db table for the _parent_ model instead.
|
|
||||||
parent_model = geo_field.model._meta.concrete_model
|
|
||||||
return self._field_column(compiler, geo_field, parent_model._meta.db_table)
|
|
||||||
else:
|
|
||||||
return self._field_column(compiler, geo_field)
|
|
||||||
|
|
||||||
# Private API utilities, subject to change.
|
|
||||||
def _geo_field(self, field_name=None):
|
|
||||||
"""
|
|
||||||
Returns the first Geometry field encountered or the one specified via
|
|
||||||
the `field_name` keyword. The `field_name` may be a string specifying
|
|
||||||
the geometry field on this GeoQuerySet's model, or a lookup string
|
|
||||||
to a geometry field via a ForeignKey relation.
|
|
||||||
"""
|
|
||||||
if field_name is None:
|
|
||||||
# Incrementing until the first geographic field is found.
|
|
||||||
for field in self.model._meta.fields:
|
|
||||||
if isinstance(field, GeometryField):
|
|
||||||
return field
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
# Otherwise, check by the given field name -- which may be
|
|
||||||
# a lookup to a _related_ geographic field.
|
|
||||||
return GISLookup._check_geo_field(self.model._meta, field_name)
|
|
||||||
|
|
||||||
def _field_column(self, compiler, field, table_alias=None, column=None):
|
|
||||||
"""
|
|
||||||
Helper function that returns the database column for the given field.
|
|
||||||
The table and column are returned (quoted) in the proper format, e.g.,
|
|
||||||
`"geoapp_city"."point"`. If `table_alias` is not specified, the
|
|
||||||
database table associated with the model of this `GeoQuerySet` will be
|
|
||||||
used. If `column` is specified, it will be used instead of the value
|
|
||||||
in `field.column`.
|
|
||||||
"""
|
|
||||||
if table_alias is None:
|
|
||||||
table_alias = compiler.query.get_meta().db_table
|
|
||||||
return "%s.%s" % (compiler.quote_name_unless_alias(table_alias),
|
|
||||||
compiler.connection.ops.quote_name(column or field.column))
|
|
|
@ -1,11 +1,9 @@
|
||||||
=========================
|
==========================
|
||||||
GeoQuerySet API Reference
|
GIS QuerySet API Reference
|
||||||
=========================
|
==========================
|
||||||
|
|
||||||
.. currentmodule:: django.contrib.gis.db.models
|
.. 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
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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'])
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
=============================
|
=============================
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue