Loading django/contrib/gis/db/backends/oracle/operations.py +38 −121 Original line number Diff line number Diff line Loading @@ -8,74 +8,47 @@ the WKT constructors. """ import re from decimal import Decimal from django.db.backends.oracle.base import DatabaseOperations from django.contrib.gis.db.backends.base import BaseSpatialOperations from django.contrib.gis.db.backends.oracle.adapter import OracleSpatialAdapter from django.contrib.gis.db.backends.utils import SpatialFunction from django.contrib.gis.db.backends.utils import SpatialOperator from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.measure import Distance from django.utils import six class SDOOperation(SpatialFunction): "Base class for SDO* Oracle operations." sql_template = "%(function)s(%(geo_col)s, %(geometry)s) %(operator)s '%(result)s'" DEFAULT_TOLERANCE = '0.05' def __init__(self, func, **kwargs): kwargs.setdefault('operator', '=') kwargs.setdefault('result', 'TRUE') super(SDOOperation, self).__init__(func, **kwargs) class SDOOperator(SpatialOperator): sql_template = "%(func)s(%(lhs)s, %(rhs)s) = 'TRUE'" class SDODistance(SpatialFunction): "Class for Distance queries." sql_template = ('%(function)s(%(geo_col)s, %(geometry)s, %(tolerance)s) ' '%(operator)s %(result)s') dist_func = 'SDO_GEOM.SDO_DISTANCE' def __init__(self, op, tolerance=0.05): super(SDODistance, self).__init__(self.dist_func, tolerance=tolerance, operator=op, result='%s') class SDODistance(SpatialOperator): sql_template = "SDO_GEOM.SDO_DISTANCE(%%(lhs)s, %%(rhs)s, %s) %%(op)s %%%%s" % DEFAULT_TOLERANCE class SDODWithin(SpatialFunction): dwithin_func = 'SDO_WITHIN_DISTANCE' sql_template = "%(function)s(%(geo_col)s, %(geometry)s, %%s) = 'TRUE'" class SDODWithin(SpatialOperator): sql_template = "SDO_WITHIN_DISTANCE(%(lhs)s, %(rhs)s, %%s) = 'TRUE'" def __init__(self): super(SDODWithin, self).__init__(self.dwithin_func) class SDODisjoint(SpatialOperator): sql_template = "SDO_GEOM.RELATE(%%(lhs)s, 'DISJOINT', %%(rhs)s, %s) = 'DISJOINT'" % DEFAULT_TOLERANCE class SDOGeomRelate(SpatialFunction): "Class for using SDO_GEOM.RELATE." relate_func = 'SDO_GEOM.RELATE' sql_template = ("%(function)s(%(geo_col)s, '%(mask)s', %(geometry)s, " "%(tolerance)s) %(operator)s '%(mask)s'") def __init__(self, mask, tolerance=0.05): # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance. # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE'). super(SDOGeomRelate, self).__init__(self.relate_func, operator='=', mask=mask, tolerance=tolerance) class SDORelate(SpatialOperator): sql_template = "SDO_RELATE(%(lhs)s, %(rhs)s, 'mask=%(mask)s') = 'TRUE'" class SDORelate(SpatialFunction): "Class for using SDO_RELATE." def check_relate_argument(self, arg): masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) sql_template = "%(function)s(%(geo_col)s, %(geometry)s, 'mask=%(mask)s') = 'TRUE'" relate_func = 'SDO_RELATE' def __init__(self, mask): if not self.mask_regex.match(mask): raise ValueError('Invalid %s mask: "%s"' % (self.relate_func, mask)) super(SDORelate, self).__init__(self.relate_func, mask=mask) if not isinstance(arg, six.string_types) or not mask_regex.match(arg): raise ValueError('Invalid SDO_RELATE mask: "%s"' % arg) # Valid distance types and substitutions dtypes = (Decimal, Distance, float) + six.integer_types def as_sql(self, connection, lookup, template_params, sql_params): template_params['mask'] = sql_params.pop() return super(SDORelate, self).as_sql(connection, lookup, template_params, sql_params) class OracleOperations(DatabaseOperations, BaseSpatialOperations): Loading Loading @@ -113,33 +86,26 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): # SDO_GEOMETRY(...) parser in Python, let me know =) select = 'SDO_UTIL.TO_WKTGEOMETRY(%s)' distance_functions = { 'distance_gt': (SDODistance('>'), dtypes), 'distance_gte': (SDODistance('>='), dtypes), 'distance_lt': (SDODistance('<'), dtypes), 'distance_lte': (SDODistance('<='), dtypes), 'dwithin': (SDODWithin(), dtypes), gis_operators = { 'contains': SDOOperator(func='SDO_CONTAINS'), 'coveredby': SDOOperator(func='SDO_COVEREDBY'), 'covers': SDOOperator(func='SDO_COVERS'), 'disjoint': SDODisjoint(), 'intersects': SDOOperator(func='SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 'equals': SDOOperator(func='SDO_EQUAL'), 'exact': SDOOperator(func='SDO_EQUAL'), 'overlaps': SDOOperator(func='SDO_OVERLAPS'), 'same_as': SDOOperator(func='SDO_EQUAL'), 'relate': SDORelate(), # Oracle uses a different syntax, e.g., 'mask=inside+touch' 'touches': SDOOperator(func='SDO_TOUCH'), 'within': SDOOperator(func='SDO_INSIDE'), 'distance_gt': SDODistance(op='>'), 'distance_gte': SDODistance(op='>='), 'distance_lt': SDODistance(op='<'), 'distance_lte': SDODistance(op='<='), 'dwithin': SDODWithin(), } geometry_functions = { 'contains': SDOOperation('SDO_CONTAINS'), 'coveredby': SDOOperation('SDO_COVEREDBY'), 'covers': SDOOperation('SDO_COVERS'), 'disjoint': SDOGeomRelate('DISJOINT'), 'intersects': SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 'equals': SDOOperation('SDO_EQUAL'), 'exact': SDOOperation('SDO_EQUAL'), 'overlaps': SDOOperation('SDO_OVERLAPS'), 'same_as': SDOOperation('SDO_EQUAL'), 'relate': (SDORelate, six.string_types), # Oracle uses a different syntax, e.g., 'mask=inside+touch' 'touches': SDOOperation('SDO_TOUCH'), 'within': SDOOperation('SDO_INSIDE'), } geometry_functions.update(distance_functions) gis_terms = {'isnull'} gis_terms.update(geometry_functions) truncate_params = {'relate': None} def geo_quote_name(self, name): Loading Loading @@ -244,55 +210,6 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): else: return 'SDO_GEOMETRY(%%s, %s)' % f.srid def check_relate_argument(self, arg): masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) if not self.mask_regex.match(arg): raise ValueError('Invalid SDO_RELATE mask: "%s"' % (self.relate_func, arg)) def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn): "Returns the SQL WHERE clause for use in Oracle spatial SQL construction." geo_col, db_type = lvalue # See if an Oracle Geometry function matches the lookup type next lookup_info = self.geometry_functions.get(lookup_type, False) if lookup_info: # Lookup types that are tuples take tuple arguments, e.g., 'relate' and # 'dwithin' lookup types. if isinstance(lookup_info, tuple): # First element of tuple is lookup type, second element is the type # of the expected argument (e.g., str, float) sdo_op, arg_type = lookup_info geom = value[0] # Ensuring that a tuple _value_ was passed in from the user if not isinstance(value, tuple): raise ValueError('Tuple required for `%s` lookup type.' % lookup_type) if len(value) != 2: raise ValueError('2-element tuple required for %s lookup type.' % lookup_type) # Ensuring the argument type matches what we expect. if not isinstance(value[1], arg_type): raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))) if lookup_type == 'relate': # The SDORelate class handles construction for these queries, # and verifies the mask argument. return sdo_op(value[1]).as_sql(geo_col, self.get_geom_placeholder(field, geom)) else: # Otherwise, just call the `as_sql` method on the SDOOperation instance. return sdo_op.as_sql(geo_col, self.get_geom_placeholder(field, geom)) else: # Lookup info is a SDOOperation instance, whose `as_sql` method returns # the SQL necessary for the geometry function call. For example: # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE' return lookup_info.as_sql(geo_col, self.get_geom_placeholder(field, value)) elif lookup_type == 'isnull': # Handling 'isnull' lookup type return "%s IS %sNULL" % (geo_col, ('' if value else 'NOT ')), [] raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) def spatial_aggregate_sql(self, agg): """ Returns the spatial aggregate SQL template and function for the Loading Loading
django/contrib/gis/db/backends/oracle/operations.py +38 −121 Original line number Diff line number Diff line Loading @@ -8,74 +8,47 @@ the WKT constructors. """ import re from decimal import Decimal from django.db.backends.oracle.base import DatabaseOperations from django.contrib.gis.db.backends.base import BaseSpatialOperations from django.contrib.gis.db.backends.oracle.adapter import OracleSpatialAdapter from django.contrib.gis.db.backends.utils import SpatialFunction from django.contrib.gis.db.backends.utils import SpatialOperator from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.measure import Distance from django.utils import six class SDOOperation(SpatialFunction): "Base class for SDO* Oracle operations." sql_template = "%(function)s(%(geo_col)s, %(geometry)s) %(operator)s '%(result)s'" DEFAULT_TOLERANCE = '0.05' def __init__(self, func, **kwargs): kwargs.setdefault('operator', '=') kwargs.setdefault('result', 'TRUE') super(SDOOperation, self).__init__(func, **kwargs) class SDOOperator(SpatialOperator): sql_template = "%(func)s(%(lhs)s, %(rhs)s) = 'TRUE'" class SDODistance(SpatialFunction): "Class for Distance queries." sql_template = ('%(function)s(%(geo_col)s, %(geometry)s, %(tolerance)s) ' '%(operator)s %(result)s') dist_func = 'SDO_GEOM.SDO_DISTANCE' def __init__(self, op, tolerance=0.05): super(SDODistance, self).__init__(self.dist_func, tolerance=tolerance, operator=op, result='%s') class SDODistance(SpatialOperator): sql_template = "SDO_GEOM.SDO_DISTANCE(%%(lhs)s, %%(rhs)s, %s) %%(op)s %%%%s" % DEFAULT_TOLERANCE class SDODWithin(SpatialFunction): dwithin_func = 'SDO_WITHIN_DISTANCE' sql_template = "%(function)s(%(geo_col)s, %(geometry)s, %%s) = 'TRUE'" class SDODWithin(SpatialOperator): sql_template = "SDO_WITHIN_DISTANCE(%(lhs)s, %(rhs)s, %%s) = 'TRUE'" def __init__(self): super(SDODWithin, self).__init__(self.dwithin_func) class SDODisjoint(SpatialOperator): sql_template = "SDO_GEOM.RELATE(%%(lhs)s, 'DISJOINT', %%(rhs)s, %s) = 'DISJOINT'" % DEFAULT_TOLERANCE class SDOGeomRelate(SpatialFunction): "Class for using SDO_GEOM.RELATE." relate_func = 'SDO_GEOM.RELATE' sql_template = ("%(function)s(%(geo_col)s, '%(mask)s', %(geometry)s, " "%(tolerance)s) %(operator)s '%(mask)s'") def __init__(self, mask, tolerance=0.05): # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance. # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE'). super(SDOGeomRelate, self).__init__(self.relate_func, operator='=', mask=mask, tolerance=tolerance) class SDORelate(SpatialOperator): sql_template = "SDO_RELATE(%(lhs)s, %(rhs)s, 'mask=%(mask)s') = 'TRUE'" class SDORelate(SpatialFunction): "Class for using SDO_RELATE." def check_relate_argument(self, arg): masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) sql_template = "%(function)s(%(geo_col)s, %(geometry)s, 'mask=%(mask)s') = 'TRUE'" relate_func = 'SDO_RELATE' def __init__(self, mask): if not self.mask_regex.match(mask): raise ValueError('Invalid %s mask: "%s"' % (self.relate_func, mask)) super(SDORelate, self).__init__(self.relate_func, mask=mask) if not isinstance(arg, six.string_types) or not mask_regex.match(arg): raise ValueError('Invalid SDO_RELATE mask: "%s"' % arg) # Valid distance types and substitutions dtypes = (Decimal, Distance, float) + six.integer_types def as_sql(self, connection, lookup, template_params, sql_params): template_params['mask'] = sql_params.pop() return super(SDORelate, self).as_sql(connection, lookup, template_params, sql_params) class OracleOperations(DatabaseOperations, BaseSpatialOperations): Loading Loading @@ -113,33 +86,26 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): # SDO_GEOMETRY(...) parser in Python, let me know =) select = 'SDO_UTIL.TO_WKTGEOMETRY(%s)' distance_functions = { 'distance_gt': (SDODistance('>'), dtypes), 'distance_gte': (SDODistance('>='), dtypes), 'distance_lt': (SDODistance('<'), dtypes), 'distance_lte': (SDODistance('<='), dtypes), 'dwithin': (SDODWithin(), dtypes), gis_operators = { 'contains': SDOOperator(func='SDO_CONTAINS'), 'coveredby': SDOOperator(func='SDO_COVEREDBY'), 'covers': SDOOperator(func='SDO_COVERS'), 'disjoint': SDODisjoint(), 'intersects': SDOOperator(func='SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 'equals': SDOOperator(func='SDO_EQUAL'), 'exact': SDOOperator(func='SDO_EQUAL'), 'overlaps': SDOOperator(func='SDO_OVERLAPS'), 'same_as': SDOOperator(func='SDO_EQUAL'), 'relate': SDORelate(), # Oracle uses a different syntax, e.g., 'mask=inside+touch' 'touches': SDOOperator(func='SDO_TOUCH'), 'within': SDOOperator(func='SDO_INSIDE'), 'distance_gt': SDODistance(op='>'), 'distance_gte': SDODistance(op='>='), 'distance_lt': SDODistance(op='<'), 'distance_lte': SDODistance(op='<='), 'dwithin': SDODWithin(), } geometry_functions = { 'contains': SDOOperation('SDO_CONTAINS'), 'coveredby': SDOOperation('SDO_COVEREDBY'), 'covers': SDOOperation('SDO_COVERS'), 'disjoint': SDOGeomRelate('DISJOINT'), 'intersects': SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 'equals': SDOOperation('SDO_EQUAL'), 'exact': SDOOperation('SDO_EQUAL'), 'overlaps': SDOOperation('SDO_OVERLAPS'), 'same_as': SDOOperation('SDO_EQUAL'), 'relate': (SDORelate, six.string_types), # Oracle uses a different syntax, e.g., 'mask=inside+touch' 'touches': SDOOperation('SDO_TOUCH'), 'within': SDOOperation('SDO_INSIDE'), } geometry_functions.update(distance_functions) gis_terms = {'isnull'} gis_terms.update(geometry_functions) truncate_params = {'relate': None} def geo_quote_name(self, name): Loading Loading @@ -244,55 +210,6 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): else: return 'SDO_GEOMETRY(%%s, %s)' % f.srid def check_relate_argument(self, arg): masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) if not self.mask_regex.match(arg): raise ValueError('Invalid SDO_RELATE mask: "%s"' % (self.relate_func, arg)) def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn): "Returns the SQL WHERE clause for use in Oracle spatial SQL construction." geo_col, db_type = lvalue # See if an Oracle Geometry function matches the lookup type next lookup_info = self.geometry_functions.get(lookup_type, False) if lookup_info: # Lookup types that are tuples take tuple arguments, e.g., 'relate' and # 'dwithin' lookup types. if isinstance(lookup_info, tuple): # First element of tuple is lookup type, second element is the type # of the expected argument (e.g., str, float) sdo_op, arg_type = lookup_info geom = value[0] # Ensuring that a tuple _value_ was passed in from the user if not isinstance(value, tuple): raise ValueError('Tuple required for `%s` lookup type.' % lookup_type) if len(value) != 2: raise ValueError('2-element tuple required for %s lookup type.' % lookup_type) # Ensuring the argument type matches what we expect. if not isinstance(value[1], arg_type): raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))) if lookup_type == 'relate': # The SDORelate class handles construction for these queries, # and verifies the mask argument. return sdo_op(value[1]).as_sql(geo_col, self.get_geom_placeholder(field, geom)) else: # Otherwise, just call the `as_sql` method on the SDOOperation instance. return sdo_op.as_sql(geo_col, self.get_geom_placeholder(field, geom)) else: # Lookup info is a SDOOperation instance, whose `as_sql` method returns # the SQL necessary for the geometry function call. For example: # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE' return lookup_info.as_sql(geo_col, self.get_geom_placeholder(field, value)) elif lookup_type == 'isnull': # Handling 'isnull' lookup type return "%s IS %sNULL" % (geo_col, ('' if value else 'NOT ')), [] raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) def spatial_aggregate_sql(self, agg): """ Returns the spatial aggregate SQL template and function for the Loading