Changeset 6919
- Timestamp:
- 12/14/07 18:30:48 (10 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/db/backend/__init__.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/db/backend/mysql/query.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/db/backend/oracle/field.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/oracle/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/backend/oracle/query.py (modified) (6 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/creation.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/field.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/postgis/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/query.py (modified) (7 diffs)
- django/branches/gis/django/contrib/gis/db/backend/util.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/models/query.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/db/backend/__init__.py
r6886 r6919 27 27 28 28 # These routines (needed by GeoManager), default to False. 29 ASGML, ASKML, DISTANCE, TRANSFORM, UNION = (False, False, False, False, False)29 ASGML, ASKML, DISTANCE, TRANSFORM, UNION, VERSION = (False, False, False, False, False, False) 30 30 31 31 if settings.DATABASE_ENGINE == 'postgresql_psycopg2': … … 35 35 PostGISField as GeoBackendField, POSTGIS_TERMS as GIS_TERMS, \ 36 36 create_spatial_db, get_geo_where_clause, \ 37 ASGML, ASKML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION 37 ASGML, ASKML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION, \ 38 MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 39 VERSION = (MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2) 38 40 SPATIAL_BACKEND = 'postgis' 39 41 elif settings.DATABASE_ENGINE == 'oracle': … … 284 286 285 287 # Getting the adapted geometry from the field. 286 gwc = get_geo_where_clause(lookup_type, current_table + '.', column, value)288 gwc = get_geo_where_clause(lookup_type, current_table, column, value) 287 289 288 290 # Substituting in the the where parameters into the geographic where django/branches/gis/django/contrib/gis/db/backend/mysql/query.py
r6527 r6919 5 5 from django.db import connection 6 6 qn = connection.ops.quote_name 7 8 # To ease implementation, WKT is passed to/from MySQL. 9 GEOM_FROM_TEXT = 'GeomFromText' 10 GEOM_FROM_WKB = 'GeomFromWKB' 11 GEOM_SELECT = 'AsText(%s)' 7 12 8 13 # WARNING: MySQL is NOT compliant w/the OpenGIS specification and … … 32 37 def get_geo_where_clause(lookup_type, table_prefix, field_name, value): 33 38 "Returns the SQL WHERE clause for use in MySQL spatial SQL construction." 34 if table_prefix.endswith('.'): 35 table_prefix = qn(table_prefix[:-1])+'.' 36 field_name = qn(field_name) 39 # Getting the quoted field as `geo_col`. 40 geo_col = '%s.%s' % (qn(table_prefix), qn(field_name)) 37 41 38 42 # See if a MySQL Geometry function matches the lookup type next 39 43 lookup_info = MYSQL_GIS_FUNCTIONS.get(lookup_type, False) 40 44 if lookup_info: 41 return "%s(%s, %%s)" % (lookup_info, table_prefix + field_name)45 return "%s(%s, %%s)" % (lookup_info, geo_col) 42 46 43 47 # Handling 'isnull' lookup type … … 45 49 # geometries in its spatial indices. 46 50 if lookup_type == 'isnull': 47 return "%s %s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))51 return "%s IS %sNULL" % (geo_col, (not value and 'NOT ' or '')) 48 52 49 53 raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) 50 51 # To ease implementation, WKT is passed to/from MySQL.52 GEOM_FROM_TEXT = 'GeomFromText'53 GEOM_FROM_WKB = 'GeomFromWKB'54 GEOM_SELECT = 'AsText(%s)'django/branches/gis/django/contrib/gis/db/backend/oracle/field.py
r6886 r6919 5 5 from django.db.models.fields import Field # Django base Field class 6 6 from django.contrib.gis.geos import GEOSGeometry 7 from django.contrib.gis.db.backend.util import get_srid,GeoFieldSQL7 from django.contrib.gis.db.backend.util import GeoFieldSQL 8 8 from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor 9 9 from django.contrib.gis.db.backend.oracle.query import ORACLE_SPATIAL_TERMS, DISTANCE_FUNCTIONS, TRANSFORM django/branches/gis/django/contrib/gis/db/backend/oracle/models.py
r6886 r6919 13 13 class GeometryColumns(models.Model): 14 14 "Maps to the Oracle USER_SDO_GEOM_METADATA table." 15 table_name = models.CharField(max length=32)16 column_name = models.CharField(max length=1024)15 table_name = models.CharField(max_length=32) 16 column_name = models.CharField(max_length=1024) 17 17 srid = models.IntegerField(primary_key=True) 18 18 # TODO: Add support for `diminfo` column (type MDSYS.SDO_DIM_ARRAY). … … 29 29 class SpatialRefSys(models.Model, SpatialRefSysMixin): 30 30 "Maps to the Oracle MDSYS.CS_SRS table." 31 cs_name = models.CharField(max length=68)31 cs_name = models.CharField(max_length=68) 32 32 srid = models.IntegerField(primary_key=True) 33 33 auth_srid = models.IntegerField() 34 auth_name = models.CharField(max length=256)35 wktext = models.CharField(max length=2046)34 auth_name = models.CharField(max_length=256) 35 wktext = models.CharField(max_length=2046) 36 36 #cs_bounds = models.GeometryField() 37 37 django/branches/gis/django/contrib/gis/db/backend/oracle/query.py
r6886 r6919 6 6 from decimal import Decimal 7 7 from django.db import connection 8 from django.contrib.gis.db.backend.util import SpatialFunction 8 9 from django.contrib.gis.measure import Distance 9 10 qn = connection.ops.quote_name … … 15 16 UNION = 'SDO_AGGR_UNION' 16 17 17 class SDOOperation(object): 18 # We want to get SDO Geometries as WKT because it is much easier to 19 # instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings. 20 # However, this adversely affects performance (i.e., Java is called 21 # to convert to WKT on every query). If someone wishes to write a 22 # SDO_GEOMETRY(...) parser in Python, let me know =) 23 GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)' 24 25 #### Classes used in constructing Oracle spatial SQL #### 26 class SDOOperation(SpatialFunction): 18 27 "Base class for SDO* Oracle operations." 28 def __init__(self, func, end_subst=") %s '%s'"): 29 super(SDOOperation, self).__init__(func, end_subst=end_subst, operator='=', result='TRUE') 19 30 20 def __init__(self, lookup, subst='', operator='=', result="'TRUE'", 21 beg_subst='%s(%s%s, %%s'): 22 self.lookup = lookup 23 self.subst = subst 24 self.operator = operator 25 self.result = result 26 self.beg_subst = beg_subst 27 self.end_subst = ') %s %s' % (self.operator, self.result) 28 29 @property 30 def sql_subst(self): 31 return ''.join([self.beg_subst, self.subst, self.end_subst]) 32 33 def as_sql(self, table, field): 34 return self.sql_subst % self.params(table, field) 35 36 def params(self, table, field): 37 return (self.lookup, table, field) 38 39 class SDODistance(SDOOperation): 31 class SDODistance(SpatialFunction): 40 32 "Class for Distance queries." 41 33 def __init__(self, op, tolerance=0.05): 42 super(SDODistance, self).__init__(DISTANCE, subst=", %s", operator=op, result='%%s')43 self.tolerance = tolerance34 super(SDODistance, self).__init__(DISTANCE, end_subst=', %s) %%s %%s' % tolerance, 35 operator=op, result='%%s') 44 36 45 def params(self, table, field): 46 return (self.lookup, table, field, self.tolerance) 47 48 class SDOGeomRelate(SDOOperation): 37 class SDOGeomRelate(SpatialFunction): 49 38 "Class for using SDO_GEOM.RELATE." 50 39 def __init__(self, mask, tolerance=0.05): 51 super(SDOGeomRelate, self).__init__('SDO_GEOM.RELATE', beg_subst="%s(%s%s, '%s'", 52 subst=", %%s, %s", result="'%s'" % mask) 53 self.mask = mask 54 self.tolerance = tolerance 40 # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance. 41 # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE'). 42 end_subst = "%s%s) %s '%s'" % (', %%s, ', tolerance, '=', mask) 43 beg_subst = "%%s(%%s, '%s'" % mask 44 super(SDOGeomRelate, self).__init__('SDO_GEOM.RELATE', beg_subst=beg_subst, end_subst=end_subst) 55 45 56 def params(self, table, field): 57 return (self.lookup, table, field, self.mask, self.tolerance) 58 59 class SDORelate(SDOOperation): 46 class SDORelate(SpatialFunction): 60 47 "Class for using SDO_RELATE." 61 48 masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' 62 49 mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) 63 64 def __init__(self, mask, **kwargs): 65 super(SDORelate, self).__init__('SDO_RELATE', subst=", 'mask=%s'", **kwargs) 50 def __init__(self, mask): 66 51 if not self.mask_regex.match(mask): 67 52 raise ValueError('Invalid %s mask: "%s"' % (self.lookup, mask)) 68 s elf.mask = mask53 super(SDORelate, self).__init__('SDO_RELATE', end_subst=", 'mask=%s') = 'TRUE'" % mask) 69 54 70 def params(self, table, field): 71 return (self.lookup, table, field, self.mask) 55 #### Lookup type mapping dictionaries of Oracle spatial operations #### 72 56 73 57 # Valid distance types and substitutions … … 85 69 'covers' : SDOOperation('SDO_COVERS'), 86 70 'disjoint' : SDOGeomRelate('DISJOINT'), 87 'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', "%%s, 'distance=%%s'"), dtypes),71 'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', end_subst=", %%s, 'distance=%%s') %s '%s'"), dtypes), 88 72 'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 89 73 'equals' : SDOOperation('SDO_EQUAL'), … … 105 89 ORACLE_SPATIAL_TERMS = tuple(ORACLE_SPATIAL_TERMS) # Making immutable 106 90 91 #### The `get_geo_where_clause` function for Oracle #### 107 92 def get_geo_where_clause(lookup_type, table_prefix, field_name, value): 108 93 "Returns the SQL WHERE clause for use in Oracle spatial SQL construction." 109 if table_prefix.endswith('.'): 110 table_prefix = qn(table_prefix[:-1])+'.' 111 field_name = qn(field_name) 94 # Getting the quoted table name as `geo_col`. 95 geo_col = '%s.%s' % (qn(table_prefix), qn(field_name)) 112 96 113 97 # See if a Oracle Geometry function matches the lookup type next … … 115 99 if lookup_info: 116 100 # Lookup types that are tuples take tuple arguments, e.g., 'relate' and 117 # 'dwithin' lookup types.101 # 'dwithin' lookup types. 118 102 if isinstance(lookup_info, tuple): 119 103 # First element of tuple is lookup type, second element is the type 120 # of the expected argument (e.g., str, float)104 # of the expected argument (e.g., str, float) 121 105 sdo_op, arg_type = lookup_info 122 106 … … 132 116 133 117 if lookup_type == 'relate': 134 # The SDORelate class handles construction for these queries, and verifies 135 # the mask argument. 136 return sdo_op(value[1]).as_sql(table_prefix, field_name) 137 elif lookup_type in DISTANCE_FUNCTIONS: 138 op = DISTANCE_FUNCTIONS[lookup_type][0] 139 return op.as_sql(table_prefix, field_name) 140 # return '%s(%s%s, %%s) %s %%s' % (DISTANCE, table_prefix, field_name, op) 118 # The SDORelate class handles construction for these queries, 119 # and verifies the mask argument. 120 return sdo_op(value[1]).as_sql(geo_col) 141 121 else: 142 return sdo_op.as_sql(table_prefix, field_name) 122 # Otherwise, just call the `as_sql` method on the SDOOperation instance. 123 return sdo_op.as_sql(geo_col) 143 124 else: 144 125 # Lookup info is a SDOOperation instance, whos `as_sql` method returns 145 126 # the SQL necessary for the geometry function call. For example: 146 127 # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE' 147 return lookup_info.as_sql(table_prefix, field_name) 148 149 # Handling 'isnull' lookup type 150 if lookup_type == 'isnull': 151 return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or '')) 128 return lookup_info.as_sql(geo_col) 129 elif lookup_type == 'isnull': 130 # Handling 'isnull' lookup type 131 return "%s IS %sNULL" % (geo_col, (not value and 'NOT ' or '')) 152 132 153 133 raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) 154 155 # Want to get SDO Geometries as WKT (much easier to instantiate GEOS proxies156 # from WKT than SDO_GEOMETRY(...) strings ;)157 GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'django/branches/gis/django/contrib/gis/db/backend/postgis/creation.py
r6439 r6919 46 46 # Trying to create the database first. 47 47 cursor.execute(create_sql) 48 #print create_sql 48 49 except Exception, e: 49 50 # Drop and recreate, if necessary. … … 57 58 else: 58 59 raise Exception('Spatial Database Creation canceled.') 60 foo = _create_with_cursor 59 61 60 62 created_regex = re.compile(r'^createdb: database creation failed: ERROR: database ".+" already exists') django/branches/gis/django/contrib/gis/db/backend/postgis/field.py
r6886 r6919 2 2 from django.db import connection 3 3 from django.db.models.fields import Field # Django base Field class 4 from django.contrib.gis.geos import GEOSGeometry , GEOSException5 from django.contrib.gis.db.backend.util import get_srid,GeoFieldSQL4 from django.contrib.gis.geos import GEOSGeometry 5 from django.contrib.gis.db.backend.util import GeoFieldSQL 6 6 from django.contrib.gis.db.backend.postgis.adaptor import PostGISAdaptor 7 7 from django.contrib.gis.db.backend.postgis.query import \ 8 DISTANCE , DISTANCE_FUNCTIONS, POSTGIS_TERMS, TRANSFORM8 DISTANCE_FUNCTIONS, POSTGIS_TERMS, TRANSFORM 9 9 10 10 # Quotename & geographic quotename, respectively django/branches/gis/django/contrib/gis/db/backend/postgis/models.py
r6886 r6919 15 15 documentation at Ch. 4.2.2. 16 16 """ 17 f_table_catalog = models.CharField(max length=256)18 f_table_schema = models.CharField(max length=256)19 f_table_name = models.CharField(max length=256)20 f_geometry_column = models.CharField(max length=256)17 f_table_catalog = models.CharField(max_length=256) 18 f_table_schema = models.CharField(max_length=256) 19 f_table_name = models.CharField(max_length=256) 20 f_geometry_column = models.CharField(max_length=256) 21 21 coord_dimension = models.IntegerField() 22 22 srid = models.IntegerField(primary_key=True) 23 type = models.CharField(max length=30)23 type = models.CharField(max_length=30) 24 24 25 25 class Meta: … … 42 42 """ 43 43 srid = models.IntegerField(primary_key=True) 44 auth_name = models.CharField(max length=256)44 auth_name = models.CharField(max_length=256) 45 45 auth_srid = models.IntegerField() 46 srtext = models.CharField(max length=2048)47 proj4text = models.CharField(max length=2048)46 srtext = models.CharField(max_length=2048) 47 proj4text = models.CharField(max_length=2048) 48 48 49 49 class Meta: django/branches/gis/django/contrib/gis/db/backend/postgis/query.py
r6886 r6919 3 3 routine for PostGIS. 4 4 """ 5 import re 5 6 from decimal import Decimal 6 7 from django.db import connection 7 8 from django.contrib.gis.measure import Distance 8 9 from django.contrib.gis.db.backend.postgis.management import postgis_version_tuple 10 from django.contrib.gis.db.backend.util import SpatialOperation, SpatialFunction 9 11 qn = connection.ops.quote_name 10 12 … … 18 20 raise Exception('PostGIS version %s not supported.' % POSTGIS_VERSION) 19 21 20 # PostGIS-specific operators. The commented descriptions of these21 # operators come from Section 6.2.2 of the official PostGIS documentation.22 POSTGIS_OPERATORS = {23 # The "&<" operator returns true if A's bounding box overlaps or24 # is to the left of B's bounding box.25 'overlaps_left' : '&<',26 # The "&>" operator returns true if A's bounding box overlaps or27 # is to the right of B's bounding box.28 'overlaps_right' : '&>',29 # The "<<" operator returns true if A's bounding box is strictly30 # to the left of B's bounding box.31 'left' : '<<',32 # The ">>" operator returns true if A's bounding box is strictly33 # to the right of B's bounding box.34 'right' : '>>',35 # The "&<|" operator returns true if A's bounding box overlaps or36 # is below B's bounding box.37 'overlaps_below' : '&<|',38 # The "|&>" operator returns true if A's bounding box overlaps or39 # is above B's bounding box.40 'overlaps_above' : '|&>',41 # The "<<|" operator returns true if A's bounding box is strictly42 # below B's bounding box.43 'strictly_below' : '<<|',44 # The "|>>" operator returns true if A's bounding box is strictly45 # above B's bounding box.46 'strictly_above' : '|>>',47 # The "~=" operator is the "same as" operator. It tests actual48 # geometric equality of two features. So if A and B are the same feature,49 # vertex-by-vertex, the operator returns true.50 'same_as' : '~=',51 'exact' : '~=',52 # The "@" operator returns true if A's bounding box is completely contained53 # by B's bounding box.54 'contained' : '@',55 # The "~" operator returns true if A's bounding box completely contains56 # by B's bounding box.57 'bbcontains' : '~',58 # The "&&" operator returns true if A's bounding box overlaps59 # B's bounding box.60 'bboverlaps' : '&&',61 }62 63 22 # Versions of PostGIS >= 1.2.2 changed their naming convention to be 64 # 'SQL-MM-centric' to conform with the ISO standard. Practically, this 23 # 'SQL-MM-centric' to conform with the ISO standard. Practically, this 65 24 # means that 'ST_' is prefixes geometry function names. 66 25 GEOM_FUNC_PREFIX = '' 67 26 if MAJOR_VERSION >= 1: 68 if (MINOR_VERSION1 > 2 or 27 if (MINOR_VERSION1 > 2 or 69 28 (MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 2)): 70 29 GEOM_FUNC_PREFIX = 'ST_' 71 30 72 31 def get_func(func): return '%s%s' % (GEOM_FUNC_PREFIX, func) 32 33 # Custom selection not needed for PostGIS since GEOS geometries may be 34 # instantiated directly from the HEXEWKB returned by default. If 35 # WKT is needed for some reason in the future, this value may be changed, 36 # 'AsText(%s)' 37 GEOM_SELECT = None 73 38 74 39 # Functions used by the GeoManager & GeoQuerySet … … 91 56 raise NotImplementedError('PostGIS versions < 1.0 are not supported.') 92 57 58 #### Classes used in constructing PostGIS spatial SQL #### 59 class PostGISOperator(SpatialOperation): 60 "For PostGIS operators (e.g. `&&`, `~`)." 61 def __init__(self, operator): 62 super(PostGISOperator, self).__init__(operator=operator, beg_subst='%s %s %%s') 63 64 class PostGISFunction(SpatialFunction): 65 "For PostGIS function calls (e.g., `ST_Contains(table, geom)`)." 66 def __init__(self, function, **kwargs): 67 super(PostGISFunction, self).__init__(get_func(function), **kwargs) 68 69 class PostGISFunctionParam(PostGISFunction): 70 "For PostGIS functions that take another parameter (e.g. DWithin, Relate)." 71 def __init__(self, func): 72 super(PostGISFunctionParam, self).__init__(func, end_subst=', %%s)') 73 74 class PostGISDistance(PostGISFunction): 75 "For PostGIS distance operations." 76 def __init__(self, operator): 77 super(PostGISDistance, self).__init__('Distance', end_subst=') %s %s', operator=operator, result='%%s') 78 79 class PostGISRelate(PostGISFunctionParam): 80 "For PostGIS Relate(<geom>, <pattern>) calls." 81 pattern_regex = re.compile(r'^[012TF\*]{9}$') 82 def __init__(self, pattern): 83 if not self.pattern_regex.match(pattern): 84 raise ValueError('Invalid intersection matrix pattern "%s".' % pattern) 85 super(PostGISRelate, self).__init__('Relate') 86 87 #### Lookup type mapping dictionaries of PostGIS operations. #### 88 89 # PostGIS-specific operators. The commented descriptions of these 90 # operators come from Section 6.2.2 of the official PostGIS documentation. 91 POSTGIS_OPERATORS = { 92 # The "&<" operator returns true if A's bounding box overlaps or 93 # is to the left of B's bounding box. 94 'overlaps_left' : PostGISOperator('&<'), 95 # The "&>" operator returns true if A's bounding box overlaps or 96 # is to the right of B's bounding box. 97 'overlaps_right' : PostGISOperator('&>'), 98 # The "<<" operator returns true if A's bounding box is strictly 99 # to the left of B's bounding box. 100 'left' : PostGISOperator('<<'), 101 # The ">>" operator returns true if A's bounding box is strictly 102 # to the right of B's bounding box. 103 'right' : PostGISOperator('>>'), 104 # The "&<|" operator returns true if A's bounding box overlaps or 105 # is below B's bounding box. 106 'overlaps_below' : PostGISOperator('&<|'), 107 # The "|&>" operator returns true if A's bounding box overlaps or 108 # is above B's bounding box. 109 'overlaps_above' : PostGISOperator('|&>'), 110 # The "<<|" operator returns true if A's bounding box is strictly 111 # below B's bounding box. 112 'strictly_below' : PostGISOperator('<<|'), 113 # The "|>>" operator returns true if A's bounding box is strictly 114 # above B's bounding box. 115 'strictly_above' : PostGISOperator('|>>'), 116 # The "~=" operator is the "same as" operator. It tests actual 117 # geometric equality of two features. So if A and B are the same feature, 118 # vertex-by-vertex, the operator returns true. 119 'same_as' : PostGISOperator('~='), 120 'exact' : PostGISOperator('~='), 121 # The "@" operator returns true if A's bounding box is completely contained 122 # by B's bounding box. 123 'contained' : PostGISOperator('@'), 124 # The "~" operator returns true if A's bounding box completely contains 125 # by B's bounding box. 126 'bbcontains' : PostGISOperator('~'), 127 # The "&&" operator returns true if A's bounding box overlaps 128 # B's bounding box. 129 'bboverlaps' : PostGISOperator('&&'), 130 } 131 93 132 # For PostGIS >= 1.2.2 the following lookup types will do a bounding box query 94 133 # first before calling the more computationally expensive GEOS routines (called … … 97 136 # 'covers'. 98 137 POSTGIS_GEOMETRY_FUNCTIONS = { 99 'equals' : 'Equals',100 'disjoint' : 'Disjoint',101 'touches' : 'Touches',102 'crosses' : 'Crosses',103 'within' : 'Within',104 'overlaps' : 'Overlaps',105 'contains' : 'Contains',106 'intersects' : 'Intersects',107 'relate' : ( 'Relate', basestring),138 'equals' : PostGISFunction('Equals'), 139 'disjoint' : PostGISFunction('Disjoint'), 140 'touches' : PostGISFunction('Touches'), 141 'crosses' : PostGISFunction('Crosses'), 142 'within' : PostGISFunction('Within'), 143 'overlaps' : PostGISFunction('Overlaps'), 144 'contains' : PostGISFunction('Contains'), 145 'intersects' : PostGISFunction('Intersects'), 146 'relate' : (PostGISRelate, basestring), 108 147 } 109 148 … … 111 150 dtypes = (Decimal, Distance, float, int) 112 151 DISTANCE_FUNCTIONS = { 113 'distance_gt' : ( '>', dtypes),114 'distance_gte' : ( '>=', dtypes),115 'distance_lt' : ( '<', dtypes),116 'distance_lte' : ( '<=', dtypes),152 'distance_gt' : (PostGISDistance('>'), dtypes), 153 'distance_gte' : (PostGISDistance('>='), dtypes), 154 'distance_lt' : (PostGISDistance('<'), dtypes), 155 'distance_lte' : (PostGISDistance('<='), dtypes), 117 156 } 118 157 119 158 if GEOM_FUNC_PREFIX == 'ST_': 120 # Adding the GEOM_FUNC_PREFIX to the lookup functions.121 for lookup, f in POSTGIS_GEOMETRY_FUNCTIONS.items():122 if isinstance(f, tuple):123 POSTGIS_GEOMETRY_FUNCTIONS[lookup] = (get_func(f[0]), f[1])124 else:125 POSTGIS_GEOMETRY_FUNCTIONS[lookup] = get_func(f)126 127 159 # The ST_DWithin, ST_CoveredBy, and ST_Covers routines become available in 1.2.2+ 128 160 POSTGIS_GEOMETRY_FUNCTIONS.update( 129 {'dwithin' : ( 'ST_DWithin', dtypes),130 'coveredby' : 'ST_CoveredBy',131 'covers' : 'ST_Covers',132 } 133 ) 134 161 {'dwithin' : (PostGISFunctionParam('DWithin'), dtypes), 162 'coveredby' : PostGISFunction('CoveredBy'), 163 'covers' : PostGISFunction('Covers'), 164 }) 165 166 # Distance functions are a part of PostGIS geometry functions. 135 167 POSTGIS_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS) 136 168 … … 145 177 POSTGIS_TERMS = tuple(POSTGIS_TERMS) # Making immutable 146 178 147 ### PostGIS-specific Methods ### 148 def get_geom_func(lookup_type): 149 func_info = POSTGIS_GEOMETRY_FUNCTIONS[lookup_type] 150 if isinstance(func_info, tuple): 151 return func_info[0] 152 else: 153 return func_info 154 179 #### The `get_geo_where_clause` function for PostGIS. #### 155 180 def get_geo_where_clause(lookup_type, table_prefix, field_name, value): 156 181 "Returns the SQL WHERE clause for use in PostGIS SQL construction." 157 if table_prefix.endswith('.'): 158 table_prefix = qn(table_prefix[:-1])+'.' 159 field_name = qn(field_name) 160 161 # See if a PostGIS operator matches the lookup type first 182 # Getting the quoted field as `geo_col`. 183 geo_col = '%s.%s' % (qn(table_prefix), qn(field_name)) 162 184 if lookup_type in POSTGIS_OPERATORS: 163 return '%s%s %s %%s' % (table_prefix, field_name, POSTGIS_OPERATORS[lookup_type]) 164 165 # See if a PostGIS Geometry function matches the lookup type next 166 if lookup_type in POSTGIS_GEOMETRY_FUNCTIONS: 167 lookup_info = POSTGIS_GEOMETRY_FUNCTIONS[lookup_type] 185 # See if a PostGIS operator matches the lookup type. 186 return POSTGIS_OPERATORS[lookup_type].as_sql(geo_col) 187 elif lookup_type in POSTGIS_GEOMETRY_FUNCTIONS: 188 # See if a PostGIS geometry function matches the lookup type. 189 tmp = POSTGIS_GEOMETRY_FUNCTIONS[lookup_type] 190 168 191 # Lookup types that are tuples take tuple arguments, e.g., 'relate' and 169 # 'dwithin' lookup types. 170 if isinstance(lookup_info, tuple): 171 # First element of tuple is lookup type, second element is the type 172 # of the expected argument (e.g., str, float) 173 func, arg_type = lookup_info 192 # distance lookups. 193 if isinstance(tmp, tuple): 194 # First element of tuple is the PostGISOperation instance, and the 195 # second element is either the type or a tuple of acceptable types 196 # that may passed in as further parameters for the lookup type. 197 op, arg_type = tmp 174 198 175 199 # Ensuring that a tuple _value_ was passed in from the user … … 183 207 raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))) 184 208 185 if lookup_type in DISTANCE_FUNCTIONS: 186 op = DISTANCE_FUNCTIONS[lookup_type][0] 187 return '%s(%s%s, %%s) %s %%s' % (DISTANCE, table_prefix, field_name, op) 188 else: 189 return "%s(%s%s, %%s, %%s)" % (func, table_prefix, field_name) 209 # For lookup type `relate`, the op instance is not yet created (has 210 # to be instantiated here to check the pattern parameter). 211 if lookup_type == 'relate': op = op(value[1]) 190 212 else: 191 # Returning the SQL necessary for the geometry function call. For example: 192 # ST_Contains("geoapp_country"."poly", ST_GeomFromWKB(..)) 193 return '%s(%s%s, %%s)' % (lookup_info, table_prefix, field_name) 194 195 # Handling 'isnull' lookup type 196 if lookup_type == 'isnull': 197 return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or '')) 213 op = tmp 214 # Calling the `as_sql` function on the operation instance. 215 return op.as_sql(geo_col) 216 elif lookup_type == 'isnull': 217 # Handling 'isnull' lookup type 218 return "%s IS %sNULL" % (geo_col, (not value and 'NOT ' or '')) 198 219 199 220 raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) 200 201 # Custom selection not needed for PostGIS since GEOS geometries may be202 # instantiated directly from the HEXEWKB returned by default. If203 # WKT is needed for some reason in the future, this value may be changed,204 # 'AsText(%s)'205 GEOM_SELECT = Nonedjango/branches/gis/django/contrib/gis/db/backend/util.py
r6886 r6919 9 9 10 10 def __str__(self): 11 return self. where[0] % tuple(self.params)11 return self.as_sql() 12 12 13 def get_srid(field, geom): 13 def as_sql(self, quote=False): 14 if not quote: 15 return self.where[0] % tuple(self.params) 16 else: 17 # Used for quoting WKT on certain backends. 18 tmp_params = ["'%s'" % self.params[0]] 19 tmp_params.extend(self.params[1:]) 20 return self.where[0] % tuple(tmp_params) 21 22 class SpatialOperation(object): 14 23 """ 15 Gets the SRID depending on the value of the SRID setting of the field 16 and that of the given geometry. 24 Base class for generating spatial SQL. 17 25 """ 18 if geom.srid is None or (geom.srid == -1 and field._srid != -1): 19 return field._srid 20 else: 21 return geom.srid 26 def __init__(self, function='', operator='', result='', beg_subst='', end_subst=''): 27 self.function = function 28 self.operator = operator 29 self.result = result 30 self.beg_subst = beg_subst 31 try: 32 # Try and put the operator and result into to the 33 # end substitution. 34 self.end_subst = end_subst % (operator, result) 35 except TypeError: 36 self.end_subst = end_subst 37 38 @property 39 def sql_subst(self): 40 return ''.join([self.beg_subst, self.end_subst]) 41 42 def as_sql(self, geo_col): 43 return self.sql_subst % self.params(geo_col) 44 45 def params(self, geo_col): 46 return (geo_col, self.operator) 47 48 class SpatialFunction(SpatialOperation): 49 """ 50 Base class for generating spatial SQL related to a function. 51 """ 52 def __init__(self, func, beg_subst='%s(%s, %%s', end_subst=')', result='', operator=''): 53 # Getting the function prefix. 54 kwargs = {'function' : func, 'operator' : operator, 'result' : result, 55 'beg_subst' : beg_subst, 'end_subst' : end_subst,} 56 super(SpatialFunction, self).__init__(**kwargs) 57 58 def params(self, geo_col): 59 return (self.function, geo_col) django/branches/gis/django/contrib/gis/db/models/query.py
r6886 r6919 8 8 # parse_lookup depends on the spatial database backend. 9 9 from django.contrib.gis.db.backend import parse_lookup, \ 10 ASGML, ASKML, DISTANCE, GEOM_SELECT, SPATIAL_BACKEND, TRANSFORM, UNION 10 ASGML, ASKML, DISTANCE, GEOM_SELECT, SPATIAL_BACKEND, TRANSFORM, UNION, VERSION 11 11 from django.contrib.gis.geos import GEOSGeometry 12 12 13 # Flag indicating whether the backend is Oracle.13 # Shortcut booleans for determining the backend. 14 14 oracle = SPATIAL_BACKEND == 'oracle' 15 postgis = SPATIAL_BACKEND == 'postgis' 15 16 16 17 class GeoQ(Q): … … 326 327 if oracle: 327 328 gml_select = {'gml':'%s(%s)' % (ASGML, field_col)} 328 else: 329 gml_select = {'gml':'%s(%s,%s,%s)' % (ASGML, field_col, precision, version)} 329 elif postgis: 330 # PostGIS AsGML() aggregate function parameter order depends on the 331 # version -- uggh. 332 major, minor1, minor2 = VERSION 333 if major >= 1 and (minor1 > 3 or (minor1 == 3 and minor2 > 1)): 334 gml_select = {'gml':'%s(%s,%s,%s)' % (ASGML, version, field_col, precision)} 335 else: 336 gml_select = {'gml':'%s(%s,%s,%s)' % (ASGML, field_col, precision, version)} 330 337 331 338 # Adding GML function call to SELECT part of the SQL.
