Skip to content

Commit

Permalink
Fixed #24214 -- Added GIS functions to replace geoqueryset's methods
Browse files Browse the repository at this point in the history
Thanks Simon Charette and Tim Graham for the reviews.
  • Loading branch information
claudep committed Apr 22, 2015
1 parent 1418f75 commit d9ff5ef
Show file tree
Hide file tree
Showing 9 changed files with 1,258 additions and 21 deletions.
19 changes: 14 additions & 5 deletions django/contrib/gis/db/backends/base/features.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from functools import partial

from django.contrib.gis.db.models import aggregates
Expand Down Expand Up @@ -59,11 +60,11 @@ def supports_relate_lookup(self):
# `has_<name>_method` (defined in __init__) which accesses connection.ops
# to determine GIS method availability.
geoqueryset_methods = (
'area', 'centroid', 'difference', 'distance', 'distance_spheroid',
'envelope', 'force_rhr', 'geohash', 'gml', 'intersection', 'kml',
'length', 'num_geom', 'perimeter', 'point_on_surface', 'reverse',
'scale', 'snap_to_grid', 'svg', 'sym_difference', 'transform',
'translate', 'union', 'unionagg',
'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',
)

# Specifies whether the Collect and Extent aggregates are supported by the database
Expand All @@ -86,5 +87,13 @@ def __init__(self, *args):
setattr(self.__class__, 'has_%s_method' % method,
property(partial(BaseSpatialFeatures.has_ops_method, method=method)))

def __getattr__(self, name):
m = re.match(r'has_(\w*)_function$', name)
if m:
func_name = m.group(1)
if func_name not in self.connection.ops.unsupported_functions:
return True
return False

This comment has been minimized.

Copy link
@blueyed

blueyed Sep 14, 2015

Contributor

@claudep
This appears to cause a regression / misbehaviour: https://code.djangoproject.com/ticket/25400


def has_ops_method(self, method):
return getattr(self.connection.ops, method, False)
25 changes: 23 additions & 2 deletions django/contrib/gis/db/backends/base/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class BaseSpatialOperations(object):
geometry = False

area = False
bounding_circle = False
centroid = False
difference = False
distance = False
Expand All @@ -30,7 +31,6 @@ class BaseSpatialOperations(object):
envelope = False
force_rhr = False
mem_size = False
bounding_circle = False
num_geom = False
num_points = False
perimeter = False
Expand All @@ -48,6 +48,22 @@ class BaseSpatialOperations(object):
# Aggregates
disallowed_aggregates = ()

geom_func_prefix = ''

# Mapping between Django function names and backend names, when names do not
# match; used in spatial_function_name().
function_names = {}

# Blacklist/set of known unsupported functions of the backend
unsupported_functions = {
'Area', 'AsGeoHash', 'AsGeoJSON', 'AsGML', 'AsKML', 'AsSVG',
'BoundingCircle', 'Centroid', 'Difference', 'Distance', 'Envelope',
'ForceRHR', 'Intersection', 'Length', 'MemSize', 'NumGeometries',
'NumPoints', 'Perimeter', 'PointOnSurface', 'Reverse', 'Scale',
'SnapToGrid', 'SymDifference', 'Transform', 'Translate',
'Union',
}

# Serialization
geohash = False
geojson = False
Expand Down Expand Up @@ -108,9 +124,14 @@ def check_expression_support(self, expression):
def spatial_aggregate_name(self, agg_name):
raise NotImplementedError('Aggregate support not implemented for this spatial backend.')

def spatial_function_name(self, func_name):
if func_name in self.unsupported_functions:
raise NotImplementedError("This backend doesn't support the %s function." % func_name)
return self.function_names.get(func_name, self.geom_func_prefix + func_name)

# Routines for getting the OGC-compliant models.
def geometry_columns(self):
raise NotImplementedError('subclasses of BaseSpatialOperations must a provide geometry_columns() method')
raise NotImplementedError('Subclasses of BaseSpatialOperations must provide a geometry_columns() method.')

def spatial_ref_sys(self):
raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_ref_sys() method')
8 changes: 6 additions & 2 deletions django/contrib/gis/db/backends/postgis/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@


class PostGISAdapter(object):
def __init__(self, geom):
def __init__(self, geom, geography=False):
"Initializes on the geometry."
# Getting the WKB (in string form, to allow easy pickling of
# the adaptor) and the SRID from the geometry.
self.ewkb = bytes(geom.ewkb)
self.srid = geom.srid
self.geography = geography
self._adapter = Binary(self.ewkb)

def __conform__(self, proto):
Expand Down Expand Up @@ -44,4 +45,7 @@ def prepare(self, conn):
def getquoted(self):
"Returns a properly quoted string for use in PostgreSQL/PostGIS."
# psycopg will figure out whether to use E'\\000' or '\000'
return str('ST_GeomFromEWKB(%s)' % self._adapter.getquoted().decode())
return str('%s(%s)' % (
'ST_GeogFromWKB' if self.geography else 'ST_GeomFromEWKB',
self._adapter.getquoted().decode())
)
7 changes: 7 additions & 0 deletions django/contrib/gis/db/backends/postgis/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
'distance_lte': PostGISDistanceOperator(func='ST_Distance', op='<=', geography=True),
}

unsupported_functions = set()
function_names = {
'BoundingCircle': 'ST_MinimumBoundingCircle',
'MemSize': 'ST_Mem_Size',
'NumPoints': 'ST_NPoints',
}

def __init__(self, connection):
super(PostGISOperations, self).__init__(connection)

Expand Down
Loading

0 comments on commit d9ff5ef

Please sign in to comment.