Changeset 7104
- Timestamp:
- 02/10/08 20:25:01 (9 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/db/backend/__init__.py (modified) (6 diffs)
- django/branches/gis/django/contrib/gis/db/backend/mysql/adaptor.py (added)
- django/branches/gis/django/contrib/gis/db/backend/mysql/field.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/db/backend/mysql/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/mysql/query.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/backend/oracle/adaptor.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/backend/oracle/field.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/db/backend/oracle/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/oracle/query.py (modified) (7 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/field.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/__init__.py (modified) (1 diff)
- 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/fields/__init__.py (modified) (6 diffs)
- django/branches/gis/django/contrib/gis/db/models/query.py (modified) (14 diffs)
- django/branches/gis/django/contrib/gis/models.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/tests/distapp/cities (deleted)
- django/branches/gis/django/contrib/gis/tests/distapp/data.py (added)
- django/branches/gis/django/contrib/gis/tests/distapp/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/tests/distapp/tests.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/db/backend/__init__.py
r7028 r7104 10 10 (3) The `parse_lookup` function, used for spatial SQL construction by 11 11 the GeoQuerySet. 12 (4) The `create_spatial_db`, and `get_geo_where_clause` 13 routines (needed by `parse_lookup`).12 (4) The `create_spatial_db`, and `get_geo_where_clause` 13 (needed by `parse_lookup`) functions. 14 14 (5) The `SpatialBackend` object, which contains information specific 15 15 to the spatial backend. 16 16 """ 17 from types import StringType, UnicodeType18 17 from django.conf import settings 19 18 from django.db import connection … … 21 20 FieldFound, LOOKUP_SEPARATOR, QUERY_TERMS 22 21 from django.utils.datastructures import SortedDict 23 from django.contrib.gis. geos import GEOSGeometry22 from django.contrib.gis.db.backend.util import gqn 24 23 25 24 # These routines (needed by GeoManager), default to False. 26 ASGML, ASKML, DISTANCE, EXTENT, TRANSFORM, UNION, VERSION = (False, False, False, False, False, False, False) 27 25 ASGML, ASKML, DISTANCE, DISTANCE_SPHEROID, EXTENT, TRANSFORM, UNION, VERSION = tuple(False for i in range(8)) 26 27 # Lookup types in which the rest of the parameters are not 28 # needed to be substitute in the WHERE SQL (e.g., the 'relate' 29 # operation on Oracle does not need the mask substituted back 30 # into the query SQL.). 31 LIMITED_WHERE = [] 32 33 # Retrieving the necessary settings from the backend. 28 34 if settings.DATABASE_ENGINE == 'postgresql_psycopg2': 29 # PostGIS is the spatial database, getting the rquired modules, 30 # renaming as necessary. 31 from django.contrib.gis.db.backend.postgis import \ 32 PostGISField as GeoBackendField, POSTGIS_TERMS as GIS_TERMS, \ 33 create_spatial_db, get_geo_where_clause, \ 34 ASGML, ASKML, DISTANCE, EXTENT, GEOM_SELECT, TRANSFORM, UNION, \ 35 from django.contrib.gis.db.backend.postgis.adaptor import \ 36 PostGISAdaptor as GeoAdaptor 37 from django.contrib.gis.db.backend.postgis.field import \ 38 PostGISField as GeoBackendField 39 from django.contrib.gis.db.backend.postgis.creation import create_spatial_db 40 from django.contrib.gis.db.backend.postgis.query import \ 41 get_geo_where_clause, POSTGIS_TERMS as GIS_TERMS, \ 42 ASGML, ASKML, DISTANCE, DISTANCE_SPHEROID, DISTANCE_FUNCTIONS, \ 43 EXTENT, GEOM_SELECT, TRANSFORM, UNION, \ 35 44 MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 45 # PostGIS version info is needed to determine calling order of some 46 # stored procedures (e.g., AsGML()). 36 47 VERSION = (MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2) 37 48 SPATIAL_BACKEND = 'postgis' 38 49 elif settings.DATABASE_ENGINE == 'oracle': 39 from django.contrib.gis.db.backend.oracle import \ 40 OracleSpatialField as GeoBackendField, \ 41 ORACLE_SPATIAL_TERMS as GIS_TERMS, \ 42 create_spatial_db, get_geo_where_clause, \ 43 ASGML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION 50 from django.contrib.gis.db.backend.oracle.adaptor import \ 51 OracleSpatialAdaptor as GeoAdaptor 52 from django.contrib.gis.db.backend.oracle.field import \ 53 OracleSpatialField as GeoBackendField 54 from django.contrib.gis.db.backend.oracle.creation import create_spatial_db 55 from django.contrib.gis.db.backend.oracle.query import \ 56 get_geo_where_clause, ORACLE_SPATIAL_TERMS as GIS_TERMS, \ 57 ASGML, DISTANCE, DISTANCE_FUNCTIONS, GEOM_SELECT, TRANSFORM, UNION 44 58 SPATIAL_BACKEND = 'oracle' 59 LIMITED_WHERE = ['relate'] 45 60 elif settings.DATABASE_ENGINE == 'mysql': 46 from django.contrib.gis.db.backend.mysql import \ 47 MySQLGeoField as GeoBackendField, \ 48 MYSQL_GIS_TERMS as GIS_TERMS, \ 49 create_spatial_db, get_geo_where_clause, \ 50 GEOM_SELECT 61 from django.contrib.gis.db.backend.mysql.adaptor import \ 62 MySQLAdaptor as GeoAdaptor 63 from django.contrib.gis.db.backend.mysql.field import \ 64 MySQLGeoField as GeoBackendField 65 from django.contrib.gis.db.backend.mysql.creation import create_spatial_db 66 from django.contrib.gis.db.backend.mysql.query import \ 67 get_geo_where_clause, MYSQL_GIS_TERMS as GIS_TERMS, GEOM_SELECT 68 DISTANCE_FUNCTIONS = {} 51 69 SPATIAL_BACKEND = 'mysql' 52 70 else: … … 54 72 55 73 class SpatialBackend(object): 56 "A container for properties of the Spatial Backend." 74 "A container for properties of the SpatialBackend." 75 # Stored procedure names used by the `GeoManager`. 57 76 as_kml = ASKML 58 77 as_gml = ASGML 59 78 distance = DISTANCE 79 distance_spheroid = DISTANCE_SPHEROID 60 80 extent = EXTENT 61 81 name = SPATIAL_BACKEND … … 63 83 transform = TRANSFORM 64 84 union = UNION 85 86 # Version information, if defined. 65 87 version = VERSION 88 89 # All valid GIS lookup terms, and distance functions. 90 gis_terms = GIS_TERMS 91 distance_functions = DISTANCE_FUNCTIONS 92 93 # Lookup types where additional WHERE parameters are excluded. 94 limited_where = LIMITED_WHERE 95 96 # Class for the backend field. 97 Field = GeoBackendField 98 99 # Adaptor class used for quoting GEOS geometries in the database. 100 Adaptor = GeoAdaptor 66 101 67 102 #### query.py overloaded functions #### … … 69 104 # counterparts to support constructing SQL for geographic queries. 70 105 # 71 # Status: Synced with r 5982.106 # Status: Synced with r7098. 72 107 # 73 108 def parse_lookup(kwarg_items, opts): … … 291 326 # with the get_geo_where_clause() 292 327 if hasattr(field, '_geom'): 293 # Getting the preparation SQL object from the field. 294 geo_prep = field.get_db_prep_lookup(lookup_type, value) 328 # Getting additional SQL WHERE and params arrays associated with 329 # the geographic field. 330 geo_where, geo_params = field.get_db_prep_lookup(lookup_type, value) 295 331 296 # Getting the adapted geometry from the field.297 gwc = get_geo_where_clause(lookup_type, current_table, column, value)298 299 # Substituting in the the where parameters into the geographic where300 # clause, and extending the parameters.301 where.append(gwc % tuple(geo_ prep.where))302 params.extend(geo_p rep.params)332 # Getting the geographic WHERE clause. 333 gwc = get_geo_where_clause(lookup_type, current_table, field, value) 334 335 # Appending the geographic WHERE componnents and parameters onto 336 # the where and params arrays. 337 where.append(gwc % tuple(geo_where)) 338 params.extend(geo_params) 303 339 else: 304 340 where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type)) django/branches/gis/django/contrib/gis/db/backend/mysql/field.py
r6527 r7104 1 import re2 from types import StringType, UnicodeType3 1 from django.db import connection 4 2 from django.db.models.fields import Field # Django base Field class 5 from django.contrib.gis.geos import GEOSGeometry 6 from django.contrib.gis.db.backend.util import GeoFieldSQL 7 from django.contrib.gis.db.backend.mysql.query import MYSQL_GIS_TERMS, GEOM_FROM_TEXT 3 from django.contrib.gis.db.backend.mysql.query import GEOM_FROM_TEXT 8 4 9 5 # Quotename & geographic quotename, respectively. 10 6 qn = connection.ops.quote_name 11 def gqn(value):12 if isinstance(value, UnicodeType): value = value.encode('ascii')13 return "'%s'" % value14 7 15 8 class MySQLGeoField(Field): … … 24 17 Thus, for best spatial performance, you should use MyISAM tables 25 18 (which do not support transactions). For more information, see Ch. 26 1 7.6.1 of the MySQL 5.0 documentation.19 16.6.1 of the MySQL 5.0 documentation. 27 20 """ 28 21 … … 51 44 "The OpenGIS name is returned for the MySQL database column type." 52 45 return self._geom 53 54 def get_db_prep_lookup(self, lookup_type, value):55 """56 Returns field's value prepared for database lookup, accepts WKT and57 GEOS Geometries for the value.58 """59 if lookup_type in MYSQL_GIS_TERMS:60 # special case for isnull lookup61 if lookup_type == 'isnull': return GeoFieldSQL([], [])62 63 # When the input is not a GEOS geometry, attempt to construct one64 # from the given string input.65 if isinstance(value, GEOSGeometry):66 pass67 elif isinstance(value, (StringType, UnicodeType)):68 try:69 value = GEOSGeometry(value)70 except GEOSException:71 raise TypeError("Could not create geometry from lookup value: %s" % str(value))72 else:73 raise TypeError('Cannot use parameter of %s type as lookup parameter.' % type(value))74 75 return GeoFieldSQL(['%s(%%s)' % GEOM_FROM_TEXT], [value])76 77 else:78 raise TypeError("Field has invalid lookup: %s" % lookup_type)79 80 def get_db_prep_save(self, value):81 "Prepares the value for saving in the database."82 if not bool(value): return None83 if isinstance(value, GEOSGeometry):84 return value85 else:86 raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')87 46 88 47 def get_placeholder(self, value): 89 48 """ 90 Nothing special happens here because MySQL does not support transformations. 49 The placeholder here has to include MySQL's WKT constructor. Because 50 MySQL does not support spatial transformations, there is no need to 51 modify the placeholder based on the contents of the given value. 91 52 """ 92 53 return '%s(%%s)' % GEOM_FROM_TEXT django/branches/gis/django/contrib/gis/db/backend/mysql/__init__.py
r6527 r7104 1 """2 The MySQL spatial database backend module.3 1 4 Please note that MySQL only supports bounding box queries, also5 known as MBRs (Minimum Bounding Rectangles). Moreover, spatial6 indices may only be used on MyISAM tables -- if you need7 transactions, take a look at PostGIS.8 """9 10 from django.contrib.gis.db.backend.mysql.creation import create_spatial_db11 from django.contrib.gis.db.backend.mysql.field import MySQLGeoField, gqn12 from django.contrib.gis.db.backend.mysql.query import get_geo_where_clause, MYSQL_GIS_TERMS, GEOM_SELECTdjango/branches/gis/django/contrib/gis/db/backend/mysql/query.py
r6919 r7104 1 1 """ 2 This module contains the spatial lookup types, and the get_geo_where_clause() 3 routine for MySQL 2 This module contains the spatial lookup types, and the `get_geo_where_clause` 3 routine for MySQL. 4 5 Please note that MySQL only supports bounding box queries, also 6 known as MBRs (Minimum Bounding Rectangles). Moreover, spatial 7 indices may only be used on MyISAM tables -- if you need 8 transactions, take a look at PostGIS. 4 9 """ 5 10 from django.db import connection … … 35 40 MYSQL_GIS_TERMS = tuple(MYSQL_GIS_TERMS) # Making immutable 36 41 37 def get_geo_where_clause(lookup_type, table_prefix, field _name, value):42 def get_geo_where_clause(lookup_type, table_prefix, field, value): 38 43 "Returns the SQL WHERE clause for use in MySQL spatial SQL construction." 39 44 # Getting the quoted field as `geo_col`. 40 geo_col = '%s.%s' % (qn(table_prefix), qn(field _name))45 geo_col = '%s.%s' % (qn(table_prefix), qn(field.column)) 41 46 42 47 # See if a MySQL Geometry function matches the lookup type next django/branches/gis/django/contrib/gis/db/backend/oracle/adaptor.py
r6524 r7104 2 2 This object provides the database adaptor for Oracle geometries. 3 3 """ 4 from cx_Oracle import CLOB5 6 4 class OracleSpatialAdaptor(object): 7 5 def __init__(self, geom): … … 12 10 "WKT is used for the substitution value of the geometry." 13 11 return self.wkt 14 15 def oracle_type(self):16 """17 The parameter type is a CLOB because no string (VARCHAR2) greater18 than 4000 characters will be accepted through the Oracle database19 API and/or SQL*Plus.20 """21 return CLOBdjango/branches/gis/django/contrib/gis/db/backend/oracle/field.py
r6919 r7104 1 import re2 from types import StringType, UnicodeType3 1 from django.db import connection 4 2 from django.db.backends.util import truncate_name 5 3 from django.db.models.fields import Field # Django base Field class 6 from django.contrib.gis.geos import GEOSGeometry 7 from django.contrib.gis.db.backend.util import GeoFieldSQL 8 from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor 9 from django.contrib.gis.db.backend.oracle.query import ORACLE_SPATIAL_TERMS, DISTANCE_FUNCTIONS, TRANSFORM 4 from django.contrib.gis.db.backend.util import gqn 5 from django.contrib.gis.db.backend.oracle.query import TRANSFORM 10 6 11 7 # Quotename & geographic quotename, respectively. 12 8 qn = connection.ops.quote_name 13 def gqn(value):14 if isinstance(value, UnicodeType): value = value.encode('ascii')15 return "'%s'" % value16 9 17 10 class OracleSpatialField(Field): … … 96 89 return 'MDSYS.SDO_GEOMETRY' 97 90 98 def get_db_prep_lookup(self, lookup_type, value):99 """100 Returns field's value prepared for database lookup, accepts WKT and101 GEOS Geometries for the value.102 """103 if lookup_type in ORACLE_SPATIAL_TERMS:104 # special case for isnull lookup105 if lookup_type == 'isnull': return GeoFieldSQL([], [])106 107 # Get the geometry with SRID; defaults SRID to that108 # of the field if it is None109 geom = self.get_geometry(value)110 111 # The adaptor will be used by psycopg2 for quoting the WKT.112 adapt = OracleSpatialAdaptor(geom)113 114 if geom.srid != self._srid:115 # Adding the necessary string substitutions and parameters116 # to perform a geometry transformation.117 where = ['%s(SDO_GEOMETRY(%%s, %s), %%s)' % (TRANSFORM, geom.srid)]118 params = [adapt, self._srid]119 else:120 where = ['SDO_GEOMETRY(%%s, %s)' % geom.srid]121 params = [adapt]122 123 if isinstance(value, tuple):124 if lookup_type in DISTANCE_FUNCTIONS or lookup_type == 'dwithin':125 # Getting the distance parameter in the units of the field126 where += [self.get_distance(value[1])]127 elif lookup_type == 'relate':128 # No extra where parameters for SDO_RELATE queries.129 pass130 else:131 where += map(gqn, value[1:])132 return GeoFieldSQL(where, params)133 else:134 raise TypeError("Field has invalid lookup: %s" % lookup_type)135 136 def get_db_prep_save(self, value):137 "Prepares the value for saving in the database."138 if not bool(value):139 # Return an empty string for NULL -- but this doesn't work yet.140 return ''141 if isinstance(value, GEOSGeometry):142 return OracleSpatialAdaptor(value)143 else:144 raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')145 146 91 def get_placeholder(self, value): 147 92 """ … … 150 95 SDO_CS.TRANSFORM() function call. 151 96 """ 152 if isinstance(value, GEOSGeometry) and value.srid != self._srid: 97 if value is None: 98 return '%s' 99 elif value.srid != self._srid: 153 100 # Adding Transform() to the SQL placeholder. 154 101 return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self._srid) 155 elif value is None:156 return '%s'157 102 else: 158 103 return 'SDO_GEOMETRY(%%s, %s)' % self._srid django/branches/gis/django/contrib/gis/db/backend/oracle/__init__.py
r6886 r7104 1 """2 The Oracle spatial database backend module.3 4 Please note that WKT support is broken on the XE version, and thus5 this backend will not work on such platforms. Specifically, XE lacks6 support for an internal JVM, and Java libraries are required to use7 the WKT constructors.8 """9 from django.contrib.gis.db.backend.oracle.creation import create_spatial_db10 from django.contrib.gis.db.backend.oracle.field import OracleSpatialField, gqn11 from django.contrib.gis.db.backend.oracle.query import \12 get_geo_where_clause, ORACLE_SPATIAL_TERMS, \13 ASGML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION14 django/branches/gis/django/contrib/gis/db/backend/oracle/query.py
r6919 r7104 1 1 """ 2 This module contains the spatial lookup types, and the get_geo_where_clause()2 This module contains the spatial lookup types, and the `get_geo_where_clause` 3 3 routine for Oracle Spatial. 4 5 Please note that WKT support is broken on the XE version, and thus 6 this backend will not work on such platforms. Specifically, XE lacks 7 support for an internal JVM, and Java libraries are required to use 8 the WKT constructors. 4 9 """ 5 10 import re … … 26 31 class SDOOperation(SpatialFunction): 27 32 "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') 33 def __init__(self, func, **kwargs): 34 kwargs.setdefault('operator', '=') 35 kwargs.setdefault('result', 'TRUE') 36 kwargs.setdefault('end_subst', ") %s '%s'") 37 super(SDOOperation, self).__init__(func, **kwargs) 30 38 31 39 class SDODistance(SpatialFunction): … … 56 64 57 65 # Valid distance types and substitutions 58 dtypes = (Decimal, Distance, float, int )66 dtypes = (Decimal, Distance, float, int, long) 59 67 DISTANCE_FUNCTIONS = { 60 68 'distance_gt' : (SDODistance('>'), dtypes), … … 62 70 'distance_lt' : (SDODistance('<'), dtypes), 63 71 'distance_lte' : (SDODistance('<='), dtypes), 72 'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', 73 beg_subst="%s(%s, %%s, 'distance=%%s'"), dtypes), 64 74 } 65 75 … … 69 79 'covers' : SDOOperation('SDO_COVERS'), 70 80 'disjoint' : SDOGeomRelate('DISJOINT'), 71 'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', end_subst=", %%s, 'distance=%%s') %s '%s'"), dtypes),72 81 'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 73 82 'equals' : SDOOperation('SDO_EQUAL'), … … 90 99 91 100 #### The `get_geo_where_clause` function for Oracle #### 92 def get_geo_where_clause(lookup_type, table_prefix, field _name, value):101 def get_geo_where_clause(lookup_type, table_prefix, field, value): 93 102 "Returns the SQL WHERE clause for use in Oracle spatial SQL construction." 94 103 # Getting the quoted table name as `geo_col`. 95 geo_col = '%s.%s' % (qn(table_prefix), qn(field _name))104 geo_col = '%s.%s' % (qn(table_prefix), qn(field.column)) 96 105 97 106 # See if a Oracle Geometry function matches the lookup type next … … 123 132 return sdo_op.as_sql(geo_col) 124 133 else: 125 # Lookup info is a SDOOperation instance, whos `as_sql` method returns134 # Lookup info is a SDOOperation instance, whose `as_sql` method returns 126 135 # the SQL necessary for the geometry function call. For example: 127 136 # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE' django/branches/gis/django/contrib/gis/db/backend/postgis/field.py
r6919 r7104 1 from types import UnicodeType2 1 from django.db import connection 3 2 from django.db.models.fields import Field # Django base Field class 4 3 from django.contrib.gis.geos import GEOSGeometry 5 from django.contrib.gis.db.backend.util import GeoFieldSQL 6 from django.contrib.gis.db.backend.postgis.adaptor import PostGISAdaptor 7 from django.contrib.gis.db.backend.postgis.query import \ 8 DISTANCE_FUNCTIONS, POSTGIS_TERMS, TRANSFORM 4 from django.contrib.gis.db.backend.util import gqn 5 from django.contrib.gis.db.backend.postgis.query import TRANSFORM 9 6 10 7 # Quotename & geographic quotename, respectively 11 8 qn = connection.ops.quote_name 12 def gqn(value):13 if isinstance(value, basestring):14 if isinstance(value, UnicodeType): value = value.encode('ascii')15 return "'%s'" % value16 else:17 return str(value)18 9 19 10 class PostGISField(Field): … … 93 84 return None 94 85 95 def get_db_prep_lookup(self, lookup_type, value):96 """97 Returns field's value prepared for database lookup, accepts WKT and98 GEOS Geometries for the value.99 """100 if lookup_type in POSTGIS_TERMS:101 # special case for isnull lookup102 if lookup_type == 'isnull': return GeoFieldSQL([], [])103 104 # Get the geometry with SRID; defaults SRID to105 # that of the field if it is None.106 geom = self.get_geometry(value)107 108 # The adaptor will be used by psycopg2 for quoting the WKB.109 adapt = PostGISAdaptor(geom)110 111 if geom.srid != self._srid:112 # Adding the necessary string substitutions and parameters113 # to perform a geometry transformation.114 where = ['%s(%%s,%%s)' % TRANSFORM]115 params = [adapt, self._srid]116 else:117 # Otherwise, the adaptor will take care of everything.118 where = ['%s']119 params = [adapt]120 121 if isinstance(value, tuple):122 if lookup_type in DISTANCE_FUNCTIONS or lookup_type == 'dwithin':123 # Getting the distance parameter in the units of the field.124 where += [self.get_distance(value[1])]125 else:126 where += map(gqn, value[1:])127 return GeoFieldSQL(where, params)128 else:129 raise TypeError("Field has invalid lookup: %s" % lookup_type)130 131 132 def get_db_prep_save(self, value):133 "Prepares the value for saving in the database."134 if not bool(value): return None135 if isinstance(value, GEOSGeometry):136 return PostGISAdaptor(value)137 else:138 raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')139 140 86 def get_placeholder(self, value): 141 87 """ … … 144 90 ST_Transform() function call. 145 91 """ 146 if isinstance(value, GEOSGeometry) and value.srid != self._srid: 92 if value is None or value.srid == self._srid: 93 return '%s' 94 else: 147 95 # Adding Transform() to the SQL placeholder. 148 96 return '%s(%%s, %s)' % (TRANSFORM, self._srid) 149 else:150 return '%s'django/branches/gis/django/contrib/gis/db/backend/postgis/__init__.py
r7028 r7104 1 """2 The PostGIS spatial database backend module.3 """4 from django.contrib.gis.db.backend.postgis.creation import create_spatial_db5 from django.contrib.gis.db.backend.postgis.field import PostGISField, gqn6 from django.contrib.gis.db.backend.postgis.query import \7 get_geo_where_clause, GEOM_FUNC_PREFIX, POSTGIS_TERMS, \8 MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2, \9 ASKML, ASGML, DISTANCE, EXTENT, GEOM_FROM_TEXT, UNION, TRANSFORM, GEOM_SELECTdjango/branches/gis/django/contrib/gis/db/backend/postgis/query.py
r7028 r7104 41 41 ASGML = get_func('AsGML') 42 42 DISTANCE = get_func('Distance') 43 DISTANCE_SPHEROID = get_func('distance_spheroid') 43 44 EXTENT = get_func('extent') 44 45 GEOM_FROM_TEXT = get_func('GeomFromText') … … 75 76 class PostGISDistance(PostGISFunction): 76 77 "For PostGIS distance operations." 78 dist_func = 'Distance' 77 79 def __init__(self, operator): 78 super(PostGISDistance, self).__init__('Distance', end_subst=') %s %s', operator=operator, result='%%s') 80 super(PostGISDistance, self).__init__(self.dist_func, end_subst=') %s %s', 81 operator=operator, result='%%s') 82 83 class PostGISSphereDistance(PostGISFunction): 84 "For PostGIS spherical distance operations." 85 dist_func = 'distance_spheroid' 86 def __init__(self, operator): 87 # An extra parameter in `end_subst` is needed for the spheroid string. 88 super(PostGISSphereDistance, self).__init__(self.dist_func, 89 beg_subst='%s(%s, %%s, %%s', 90 end_subst=') %s %s', 91 operator=operator, result='%%s') 79 92 80 93 class PostGISRelate(PostGISFunctionParam): … … 149 162 150 163 # Valid distance types and substitutions 151 dtypes = (Decimal, Distance, float, int) 164 dtypes = (Decimal, Distance, float, int, long) 165 def get_dist_ops(operator): 166 "Returns operations for both regular and spherical distances." 167 return (PostGISDistance(operator), PostGISSphereDistance(operator)) 152 168 DISTANCE_FUNCTIONS = { 153 'distance_gt' : ( PostGISDistance('>'), dtypes),154 'distance_gte' : ( PostGISDistance('>='), dtypes),155 'distance_lt' : ( PostGISDistance('<'), dtypes),156 'distance_lte' : ( PostGISDistance('<='), dtypes),169 'distance_gt' : (get_dist_ops('>'), dtypes), 170 'distance_gte' : (get_dist_ops('>='), dtypes), 171 'distance_lt' : (get_dist_ops('<'), dtypes), 172 'distance_lte' : (get_dist_ops('<='), dtypes), 157 173 } 158 174 … … 160 176 # The ST_DWithin, ST_CoveredBy, and ST_Covers routines become available in 1.2.2+ 161 177 POSTGIS_GEOMETRY_FUNCTIONS.update( 162 {'dwithin' : (PostGISFunctionParam('DWithin'), dtypes), 163 'coveredby' : PostGISFunction('CoveredBy'), 178 {'coveredby' : PostGISFunction('CoveredBy'), 164 179 'covers' : PostGISFunction('Covers'), 165 180 }) 181 DISTANCE_FUNCTIONS['dwithin'] = (PostGISFunctionParam('DWithin'), dtypes) 166 182 167 183 # Distance functions are a part of PostGIS geometry functions. … … 179 195 180 196 #### The `get_geo_where_clause` function for PostGIS. #### 181 def get_geo_where_clause(lookup_type, table_prefix, field _name, value):197 def get_geo_where_clause(lookup_type, table_prefix, field, value): 182 198 "Returns the SQL WHERE clause for use in PostGIS SQL construction." 183 199 # Getting the quoted field as `geo_col`. 184 geo_col = '%s.%s' % (qn(table_prefix), qn(field _name))200 geo_col = '%s.%s' % (qn(table_prefix), qn(field.column)) 185 201 if lookup_type in POSTGIS_OPERATORS: 186 202 # See if a PostGIS operator matches the lookup type. … … 199 215 200 216 # Ensuring that a tuple _value_ was passed in from the user 201 if not isinstance(value, tuple):217 if not isinstance(value, (tuple, list)): 202 218 raise TypeError('Tuple required for `%s` lookup type.' % lookup_type) 203 219 if len(value) != 2: … … 210 226 # For lookup type `relate`, the op instance is not yet created (has 211 227 # to be instantiated here to check the pattern parameter). 212 if lookup_type == 'relate': op = op(value[1]) 228 if lookup_type == 'relate': 229 op = op(value[1]) 230 elif lookup_type in DISTANCE_FUNCTIONS and lookup_type != 'dwithin': 231 if field._unit_name == 'degree': 232 # Geodetic distances are only availble from Points to PointFields. 233 if field._geom != 'POINT': 234 raise TypeError('PostGIS spherical operations are only valid on PointFields.') 235 if value[0].geom_typeid != 0: 236 raise TypeError('PostGIS geometry distance parameter is required to be of type Point.') 237 op = op[1] 238 else: 239 op = op[0] 213 240 else: 214 241 op = tmp django/branches/gis/django/contrib/gis/db/backend/util.py
r6919 r7104 1 class GeoFieldSQL(object): 1 from types import UnicodeType 2 3 def gqn(val): 2 4 """ 3 Container for passing values to `parse_lookup` from the various 4 backend geometry fields. 5 The geographic quote name function; used for quoting tables and 6 geometries (they use single rather than the double quotes of the 7 backend quotename function). 5 8 """ 6 def __init__(self, where=[], params=[]): 7 self.where = where 8 self.params = params 9 10 def __str__(self): 11 return self.as_sql() 12 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) 9 if isinstance(val, basestring): 10 if isinstance(val, UnicodeType): val = val.encode('ascii') 11 return "'%s'" % val 12 else: 13 return str(val) 21 14 22 15 class SpatialOperation(object): django/branches/gis/django/contrib/gis/db/models/fields/__init__.py
r6886 r7104 2 2 from django.conf import settings 3 3 from django.db import connection 4 from django.contrib.gis.db.backend import GeoBackendField # these depend on the spatial database backend. 4 # Getting the SpatialBackend container and the geographic quoting method. 5 from django.contrib.gis.db.backend import SpatialBackend, gqn 6 # GeometryProxy, GEOS, Distance, and oldforms imports. 5 7 from django.contrib.gis.db.models.proxy import GeometryProxy 6 8 from django.contrib.gis.geos import GEOSException, GEOSGeometry … … 15 17 16 18 #TODO: Flesh out widgets; consider adding support for OGR Geometry proxies. 17 class GeometryField( GeoBackendField):19 class GeometryField(SpatialBackend.Field): 18 20 "The base GIS field -- maps to the OpenGIS Specification Geometry type." 19 21 … … 39 41 """ 40 42 41 # Backward-compatibility notice, this will disappear in future revisions. 42 if 'index' in kwargs: 43 from warnings import warn 44 warn('The `index` keyword has been deprecated, please use the `spatial_index` keyword instead.') 45 self._index = kwargs['index'] 46 else: 47 self._index = spatial_index 48 49 # Setting the SRID and getting the units. 43 # Setting the index flag with the value of the `spatial_index` keyword. 44 self._index = spatial_index 45 46 # Setting the SRID and getting the units. Unit information must be 47 # easily available in the field instance for distance queries. 50 48 self._srid = srid 51 49 if SpatialRefSys: 52 # This doesn't work when we actually use: SpatialRefSys.objects.get(srid=srid) 50 # Getting the spatial reference WKT associated with the SRID from the 51 # `spatial_ref_sys` (or equivalent) spatial database table. 52 # 53 # The following doesn't work: SpatialRefSys.objects.get(srid=srid) 53 54 # Why? `syncdb` fails to recognize installed geographic models when there's 54 # an ORM query instantiated within a model field. No matter, this works fine 55 # too. 55 # an ORM query instantiated within a model field. 56 56 cur = connection.cursor() 57 57 qn = connection.ops.quote_name … … 63 63 } 64 64 cur.execute(stmt) 65 row = cur.fetchone() 66 self._unit, self._unit_name = SpatialRefSys.get_units(row[0]) 65 srs_wkt = cur.fetchone()[0] 67 66 67 # Getting metadata associated with the spatial reference system identifier. 68 # Specifically, getting the unit information and spheroid information 69 # (both required for distance queries). 70 self._unit, self._unit_name = SpatialRefSys.get_units(srs_wkt) 71 self._spheroid = SpatialRefSys.get_spheroid(srs_wkt) 72 68 73 # Setting the dimension of the geometry field. 69 74 self._dim = dim 75 70 76 super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function 71 77 72 78 ### Routines specific to GeometryField ### 73 79 def get_distance(self, dist): 80 """ 81 Returns a distance number in units of the field. For example, if 82 `D(km=1)` was passed in and the units of the field were in meters, 83 then 1000 would be returned. 84 """ 74 85 if isinstance(dist, Distance): 75 return getattr(dist, Distance.unit_attname(self._unit_name)) 76 elif isinstance(dist, (int, float, Decimal)): 86 if self._unit_name in ('Decimal Degree', 'degree'): 87 # Spherical distance calculation parameter should be in meters. 88 dist_param = dist.m 89 else: 90 dist_param = getattr(dist, Distance.unit_attname(self._unit_name)) 91 else: 77 92 # Assuming the distance is in the units of the field. 78 return dist 93 dist_param = dist 94 95 # Sphereical distance query; returning meters. 96 if SpatialBackend.name == 'postgis' and self._unit_name == 'degree': 97 return [gqn(self._spheroid), dist_param] 98 else: 99 return [dist_param] 79 100 80 101 def get_geometry(self, value): … … 83 104 lookup parameters. 84 105 """ 85 if isinstance(value, tuple):106 if isinstance(value, (tuple, list)): 86 107 geom = value[0] 87 108 else: … … 122 143 setattr(cls, self.attname, GeometryProxy(GEOSGeometry, self)) 123 144 145 def get_db_prep_lookup(self, lookup_type, value): 146 """ 147 Returns the spatial WHERE clause and associated parameters for the 148 given lookup type and value. The value will be prepared for database 149 lookup (e.g., spatial transformation SQL will be added if necessary). 150 """ 151 if lookup_type in SpatialBackend.gis_terms: 152 # special case for isnull lookup 153 if lookup_type == 'isnull': return [], [] 154 155 # Get the geometry with SRID; defaults SRID to that of the field 156 # if it is None. 157 geom = self.get_geometry(value) &nbs
