Changeset 6886
- Timestamp:
- 12/04/07 11:44:37 (8 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/db/backend/__init__.py (modified) (6 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/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/backend/oracle/query.py (modified) (4 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/adaptor.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/postgis/field.py (modified) (4 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/postgis/models.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/db/backend/postgis/query.py (modified) (5 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) (2 diffs)
- django/branches/gis/django/contrib/gis/db/models/manager.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/models/query.py (modified) (14 diffs)
- django/branches/gis/django/contrib/gis/models.py (modified) (5 diffs)
- django/branches/gis/django/contrib/gis/tests/distapp (added)
- django/branches/gis/django/contrib/gis/tests/distapp/cities (added)
- django/branches/gis/django/contrib/gis/tests/distapp/cities/cities.dbf (added)
- django/branches/gis/django/contrib/gis/tests/distapp/cities/cities.prj (added)
- django/branches/gis/django/contrib/gis/tests/distapp/cities/cities.shp (added)
- django/branches/gis/django/contrib/gis/tests/distapp/cities/cities.shx (added)
- django/branches/gis/django/contrib/gis/tests/distapp/__init__.py (added)
- django/branches/gis/django/contrib/gis/tests/distapp/models.py (added)
- django/branches/gis/django/contrib/gis/tests/distapp/tests.py (added)
- django/branches/gis/django/contrib/gis/tests/geoapp/models.py (modified) (4 diffs)
- django/branches/gis/django/contrib/gis/tests/geoapp/sql/city.oracle.sql (added)
- django/branches/gis/django/contrib/gis/tests/geoapp/tests.py (modified) (14 diffs)
- django/branches/gis/django/contrib/gis/tests/__init__.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/db/backend/__init__.py
r6527 r6886 27 27 28 28 # These routines (needed by GeoManager), default to False. 29 ASGML, ASKML, TRANSFORM, UNION= (False, False, False, False)29 ASGML, ASKML, DISTANCE, TRANSFORM, UNION= (False, False, False, False, False) 30 30 31 31 if settings.DATABASE_ENGINE == 'postgresql_psycopg2': … … 34 34 from django.contrib.gis.db.backend.postgis import \ 35 35 PostGISField as GeoBackendField, POSTGIS_TERMS as GIS_TERMS, \ 36 create_spatial_db, get_geo_where_clause, gqn,\37 ASGML, ASKML, GEOM_SELECT, TRANSFORM, UNION36 create_spatial_db, get_geo_where_clause, \ 37 ASGML, ASKML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION 38 38 SPATIAL_BACKEND = 'postgis' 39 39 elif settings.DATABASE_ENGINE == 'oracle': … … 41 41 OracleSpatialField as GeoBackendField, \ 42 42 ORACLE_SPATIAL_TERMS as GIS_TERMS, \ 43 create_spatial_db, get_geo_where_clause, gqn,\44 ASGML, GEOM_SELECT, TRANSFORM, UNION43 create_spatial_db, get_geo_where_clause, \ 44 ASGML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION 45 45 SPATIAL_BACKEND = 'oracle' 46 46 elif settings.DATABASE_ENGINE == 'mysql': … … 48 48 MySQLGeoField as GeoBackendField, \ 49 49 MYSQL_GIS_TERMS as GIS_TERMS, \ 50 create_spatial_db, get_geo_where_clause, gqn,\50 create_spatial_db, get_geo_where_clause, \ 51 51 GEOM_SELECT 52 52 SPATIAL_BACKEND = 'mysql' 53 53 else: 54 54 raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE) 55 56 def geo_quotename(value):57 """58 Returns the quotation used on a given Geometry value using the geometry59 quoting from the backend (the `gqn` function).60 """61 if isinstance(value, (StringType, UnicodeType)): return gqn(value)62 else: return str(value)63 55 64 56 #### query.py overloaded functions #### … … 118 110 elif callable(value): 119 111 value = value() 120 112 121 113 joins2, where2, params2 = lookup_inner(path, lookup_type, value, opts, opts.db_table, None) 122 114 joins.update(joins2) … … 288 280 # with the get_geo_where_clause() 289 281 if hasattr(field, '_geom'): 290 # Do we have multiple arguments, e.g., `relate`, `dwithin` lookup types291 # need more than argument.292 multiple_args = isinstance(value, tuple)293 294 282 # Getting the preparation SQL object from the field. 295 if multiple_args: 296 geo_prep = field.get_db_prep_lookup(lookup_type, value[0]) 297 else: 298 geo_prep = field.get_db_prep_lookup(lookup_type, value) 299 283 geo_prep = field.get_db_prep_lookup(lookup_type, value) 284 300 285 # Getting the adapted geometry from the field. 301 286 gwc = get_geo_where_clause(lookup_type, current_table + '.', column, value) 302 303 # A GeoFieldSQL object is returned by `get_db_prep_lookup` -- 304 # getting the substitution list and the geographic parameters. 305 subst_list = geo_prep.where 306 if multiple_args: subst_list += map(geo_quotename, value[1:]) 307 gwc = gwc % tuple(subst_list) 308 309 # Finally, appending onto the WHERE clause, and extending with 310 # the additional parameters. 311 where.append(gwc) 287 288 # Substituting in the the where parameters into the geographic where 289 # clause, and extending the parameters. 290 where.append(gwc % tuple(geo_prep.where)) 312 291 params.extend(geo_prep.params) 313 292 else: django/branches/gis/django/contrib/gis/db/backend/oracle/field.py
r6596 r6886 7 7 from django.contrib.gis.db.backend.util import get_srid, GeoFieldSQL 8 8 from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor 9 from django.contrib.gis.db.backend.oracle.query import ORACLE_SPATIAL_TERMS, TRANSFORM9 from django.contrib.gis.db.backend.oracle.query import ORACLE_SPATIAL_TERMS, DISTANCE_FUNCTIONS, TRANSFORM 10 10 11 11 # Quotename & geographic quotename, respectively. … … 22 22 empty_strings_allowed = False 23 23 24 def __init__(self, extent=(-180.0, -90.0, 180.0, 90.0), tolerance=0.0 0005, **kwargs):24 def __init__(self, extent=(-180.0, -90.0, 180.0, 90.0), tolerance=0.05, **kwargs): 25 25 """ 26 26 Oracle Spatial backend needs to have the extent -- for projected coordinate 27 27 systems _you must define the extent manually_, since the coordinates are 28 28 for geodetic systems. The `tolerance` keyword specifies the tolerance 29 for error (in meters) .29 for error (in meters), and defaults to 0.05 (5 centimeters). 30 30 """ 31 31 # Oracle Spatial specific keyword arguments. … … 105 105 if lookup_type == 'isnull': return GeoFieldSQL([], []) 106 106 107 # When the input is not a GEOS geometry, attempt to construct one 108 # from the given string input. 109 if isinstance(value, GEOSGeometry): 110 pass 111 elif isinstance(value, (StringType, UnicodeType)): 112 try: 113 value = GEOSGeometry(value) 114 except GEOSException: 115 raise TypeError("Could not create geometry from lookup value: %s" % str(value)) 116 else: 117 raise TypeError('Cannot use parameter of %s type as lookup parameter.' % type(value)) 118 119 # Getting the SRID of the geometry, or defaulting to that of the field if 120 # it is None. 121 srid = get_srid(self, value) 107 # Get the geometry with SRID; defaults SRID to that 108 # of the field if it is None 109 geom = self.get_geometry(value) 122 110 123 111 # The adaptor will be used by psycopg2 for quoting the WKT. 124 adapt = OracleSpatialAdaptor(value) 125 if srid != self._srid: 112 adapt = OracleSpatialAdaptor(geom) 113 114 if geom.srid != self._srid: 126 115 # Adding the necessary string substitutions and parameters 127 116 # to perform a geometry transformation. 128 return GeoFieldSQL(['%s(SDO_GEOMETRY(%%s, %s), %%s)' % (TRANSFORM, srid)],129 [adapt, self._srid])117 where = ['%s(SDO_GEOMETRY(%%s, %s), %%s)' % (TRANSFORM, geom.srid)] 118 params = [adapt, self._srid] 130 119 else: 131 return GeoFieldSQL(['SDO_GEOMETRY(%%s, %s)' % srid], [adapt]) 120 where = ['SDO_GEOMETRY(%%s, %s)' % geom.srid] 121 params = [adapt] 132 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 field 126 where += [self.get_distance(value[1])] 127 elif lookup_type == 'relate': 128 # No extra where parameters for SDO_RELATE queries. 129 pass 130 else: 131 where += map(gqn, value[1:]) 132 return GeoFieldSQL(where, params) 133 133 else: 134 134 raise TypeError("Field has invalid lookup: %s" % lookup_type) django/branches/gis/django/contrib/gis/db/backend/oracle/__init__.py
r6527 r6886 11 11 from django.contrib.gis.db.backend.oracle.query import \ 12 12 get_geo_where_clause, ORACLE_SPATIAL_TERMS, \ 13 ASGML, GEOM_SELECT, TRANSFORM, UNION13 ASGML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION 14 14 django/branches/gis/django/contrib/gis/db/backend/oracle/models.py
r6524 r6886 21 21 22 22 @classmethod 23 def table_name_col( self):23 def table_name_col(cls): 24 24 return 'table_name' 25 25 … … 44 44 def wkt(self): 45 45 return self.wktext 46 47 @classmethod 48 def wkt_col(cls): 49 return 'wktext' django/branches/gis/django/contrib/gis/db/backend/oracle/query.py
r6527 r6886 3 3 routine for Oracle Spatial. 4 4 """ 5 import re 6 from decimal import Decimal 5 7 from django.db import connection 8 from django.contrib.gis.measure import Distance 6 9 qn = connection.ops.quote_name 7 10 11 # The GML, distance, transform, and union procedures. 12 ASGML = 'SDO_UTIL.TO_GMLGEOMETRY' 13 DISTANCE = 'SDO_GEOM.SDO_DISTANCE' 14 TRANSFORM = 'SDO_CS.TRANSFORM' 15 UNION = 'SDO_AGGR_UNION' 16 17 class SDOOperation(object): 18 "Base class for SDO* Oracle operations." 19 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): 40 "Class for Distance queries." 41 def __init__(self, op, tolerance=0.05): 42 super(SDODistance, self).__init__(DISTANCE, subst=", %s", operator=op, result='%%s') 43 self.tolerance = tolerance 44 45 def params(self, table, field): 46 return (self.lookup, table, field, self.tolerance) 47 48 class SDOGeomRelate(SDOOperation): 49 "Class for using SDO_GEOM.RELATE." 50 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 55 56 def params(self, table, field): 57 return (self.lookup, table, field, self.mask, self.tolerance) 58 59 class SDORelate(SDOOperation): 60 "Class for using SDO_RELATE." 61 masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON' 62 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) 66 if not self.mask_regex.match(mask): 67 raise ValueError('Invalid %s mask: "%s"' % (self.lookup, mask)) 68 self.mask = mask 69 70 def params(self, table, field): 71 return (self.lookup, table, field, self.mask) 72 73 # Valid distance types and substitutions 74 dtypes = (Decimal, Distance, float, int) 75 DISTANCE_FUNCTIONS = { 76 'distance_gt' : (SDODistance('>'), dtypes), 77 'distance_gte' : (SDODistance('>='), dtypes), 78 'distance_lt' : (SDODistance('<'), dtypes), 79 'distance_lte' : (SDODistance('<='), dtypes), 80 } 81 8 82 ORACLE_GEOMETRY_FUNCTIONS = { 9 'contains' : 'SDO_CONTAINS',10 'coveredby' : 'SDO_COVEREDBY',11 'covers' : 'SDO_COVERS',12 'disjoint' : 'SDO_DISJOINT',13 'dwithin' : ( 'SDO_WITHIN_DISTANCE', float),14 'intersects' : 'SDO_OVERLAPBDYINTERSECT', # TODO: Is this really the same as ST_Intersects()?15 'equals' : 'SDO_EQUAL',16 'exact' : 'SDO_EQUAL',17 'overlaps' : 'SDO_OVERLAPS',18 'same_as' : 'SDO_EQUAL',19 #'relate' : ('SDO_RELATE', str), # Oracle uses a different syntax, e.g., 'mask=inside+touch'20 'touches' : 'SDO_TOUCH',21 'within' : 'SDO_INSIDE',83 'contains' : SDOOperation('SDO_CONTAINS'), 84 'coveredby' : SDOOperation('SDO_COVEREDBY'), 85 'covers' : SDOOperation('SDO_COVERS'), 86 'disjoint' : SDOGeomRelate('DISJOINT'), 87 'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', "%%s, 'distance=%%s'"), dtypes), 88 'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()? 89 'equals' : SDOOperation('SDO_EQUAL'), 90 'exact' : SDOOperation('SDO_EQUAL'), 91 'overlaps' : SDOOperation('SDO_OVERLAPS'), 92 'same_as' : SDOOperation('SDO_EQUAL'), 93 'relate' : (SDORelate, basestring), # Oracle uses a different syntax, e.g., 'mask=inside+touch' 94 'touches' : SDOOperation('SDO_TOUCH'), 95 'within' : SDOOperation('SDO_INSIDE'), 22 96 } 97 ORACLE_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS) 23 98 24 99 # This lookup type does not require a mapping. … … 44 119 # First element of tuple is lookup type, second element is the type 45 120 # of the expected argument (e.g., str, float) 46 func, arg_type = lookup_info121 sdo_op, arg_type = lookup_info 47 122 48 123 # Ensuring that a tuple _value_ was passed in from the user 49 if not isinstance(value, tuple) or len(value) != 2: 50 raise TypeError('2-element tuple required for %s lookup type.' % lookup_type) 124 if not isinstance(value, tuple): 125 raise TypeError('Tuple required for `%s` lookup type.' % lookup_type) 126 if len(value) != 2: 127 raise ValueError('2-element tuple required for %s lookup type.' % lookup_type) 51 128 52 129 # Ensuring the argument type matches what we expect. … … 54 131 raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))) 55 132 56 if func == 'dwithin': 57 # TODO: test and consider adding different distance options. 58 return "%s(%s, %%s, 'distance=%s')" % (func, table_prefix + field_name, value[1]) 133 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) 59 141 else: 60 return "%s(%s, %%s, %%s) = 'TRUE'" % (func, table_prefix +field_name)142 return sdo_op.as_sql(table_prefix, field_name) 61 143 else: 62 # Returning the SQL necessary for the geometry function call. For example: 144 # Lookup info is a SDOOperation instance, whos `as_sql` method returns 145 # the SQL necessary for the geometry function call. For example: 63 146 # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE' 64 return "%s(%s, %%s) = 'TRUE'" % (lookup_info, table_prefix +field_name)147 return lookup_info.as_sql(table_prefix, field_name) 65 148 66 149 # Handling 'isnull' lookup type … … 70 153 raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) 71 154 72 ASGML = 'SDO_UTIL.TO_GMLGEOMETRY'73 UNION = 'SDO_AGGR_UNION'74 TRANSFORM = 'SDO_CS.TRANSFORM'75 76 155 # Want to get SDO Geometries as WKT (much easier to instantiate GEOS proxies 77 156 # from WKT than SDO_GEOMETRY(...) strings ;) django/branches/gis/django/contrib/gis/db/backend/postgis/adaptor.py
r6508 r6886 8 8 9 9 class PostGISAdaptor(object): 10 def __init__(self, geom , srid):10 def __init__(self, geom): 11 11 "Initializes on the geometry and the SRID." 12 12 # Getting the WKB and the SRID 13 13 self.wkb = geom.wkb 14 self.srid = srid14 self.srid = geom.srid 15 15 16 16 def __conform__(self, proto): django/branches/gis/django/contrib/gis/db/backend/postgis/field.py
r6596 r6886 1 from types import StringType,UnicodeType1 from types import UnicodeType 2 2 from django.db import connection 3 3 from django.db.models.fields import Field # Django base Field class … … 5 5 from django.contrib.gis.db.backend.util import get_srid, GeoFieldSQL 6 6 from django.contrib.gis.db.backend.postgis.adaptor import PostGISAdaptor 7 from django.contrib.gis.db.backend.postgis.query import POSTGIS_TERMS, TRANSFORM8 from psycopg2 import Binary 7 from django.contrib.gis.db.backend.postgis.query import \ 8 DISTANCE, DISTANCE_FUNCTIONS, POSTGIS_TERMS, TRANSFORM 9 9 10 10 # Quotename & geographic quotename, respectively 11 11 qn = connection.ops.quote_name 12 12 def gqn(value): 13 if isinstance(value, UnicodeType): value = value.encode('ascii') 14 return "'%s'" % value 13 if isinstance(value, basestring): 14 if isinstance(value, UnicodeType): value = value.encode('ascii') 15 return "'%s'" % value 16 else: 17 return str(value) 15 18 16 19 class PostGISField(Field): 20 """ 21 The backend-specific geographic field for PostGIS. 22 """ 23 17 24 def _add_geom(self, style, db_table): 18 25 """ … … 93 100 if lookup_type in POSTGIS_TERMS: 94 101 # special case for isnull lookup 95 if lookup_type == 'isnull': 96 return GeoFieldSQL([], [value]) 102 if lookup_type == 'isnull': return GeoFieldSQL([], []) 97 103 98 # When the input is not a GEOS geometry, attempt to construct one 99 # from the given string input. 100 if isinstance(value, GEOSGeometry): 101 pass 102 elif isinstance(value, (StringType, UnicodeType)): 103 try: 104 value = GEOSGeometry(value) 105 except GEOSException: 106 raise TypeError("Could not create geometry from lookup value: %s" % str(value)) 107 else: 108 raise TypeError('Cannot use parameter of %s type as lookup parameter.' % type(value)) 109 110 # Getting the SRID of the geometry, or defaulting to that of the field if 111 # it is None. 112 srid = get_srid(self, value) 104 # Get the geometry with SRID; defaults SRID to 105 # that of the field if it is None. 106 geom = self.get_geometry(value) 113 107 114 108 # The adaptor will be used by psycopg2 for quoting the WKB. 115 adapt = PostGISAdaptor( value, srid)109 adapt = PostGISAdaptor(geom) 116 110 117 if srid != self._srid:111 if geom.srid != self._srid: 118 112 # Adding the necessary string substitutions and parameters 119 113 # to perform a geometry transformation. 120 return GeoFieldSQL(['%s(%%s,%%s)' % TRANSFORM],121 [adapt, self._srid])114 where = ['%s(%%s,%%s)' % TRANSFORM] 115 params = [adapt, self._srid] 122 116 else: 123 return GeoFieldSQL(['%s'], [adapt]) 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) 124 128 else: 125 129 raise TypeError("Field has invalid lookup: %s" % lookup_type) 130 126 131 127 132 def get_db_prep_save(self, value): … … 129 134 if not bool(value): return None 130 135 if isinstance(value, GEOSGeometry): 131 return PostGISAdaptor(value , value.srid)136 return PostGISAdaptor(value) 132 137 else: 133 138 raise TypeError('Geometry Proxy should only return GEOSGeometry objects.') 134 135 def get_internal_type(self):136 """137 Returns NoField because a stored procedure is used by PostGIS to create138 the Geometry Fields.139 """140 return 'NoField'141 139 142 140 def get_placeholder(self, value): django/branches/gis/django/contrib/gis/db/backend/postgis/__init__.py
r6509 r6886 7 7 get_geo_where_clause, GEOM_FUNC_PREFIX, POSTGIS_TERMS, \ 8 8 MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2, \ 9 ASKML, ASGML, GEOM_FROM_TEXT, UNION, TRANSFORM, GEOM_SELECT9 ASKML, ASGML, DISTANCE, GEOM_FROM_TEXT, UNION, TRANSFORM, GEOM_SELECT django/branches/gis/django/contrib/gis/db/backend/postgis/models.py
r6524 r6886 17 17 f_table_catalog = models.CharField(maxlength=256) 18 18 f_table_schema = models.CharField(maxlength=256) 19 f_table_name = models.CharField(maxlength=256 , primary_key=True)19 f_table_name = models.CharField(maxlength=256) 20 20 f_geometry_column = models.CharField(maxlength=256) 21 21 coord_dimension = models.IntegerField() 22 srid = models.IntegerField( )22 srid = models.IntegerField(primary_key=True) 23 23 type = models.CharField(maxlength=30) 24 24 … … 27 27 28 28 @classmethod 29 def table_name_col( self):29 def table_name_col(cls): 30 30 "Class method for returning the table name column for this model." 31 31 return 'f_table_name' … … 53 53 def wkt(self): 54 54 return self.srtext 55 56 @classmethod 57 def wkt_col(cls): 58 return 'srtext' django/branches/gis/django/contrib/gis/db/backend/postgis/query.py
r6524 r6886 3 3 routine for PostGIS. 4 4 """ 5 from decimal import Decimal 5 6 from django.db import connection 7 from django.contrib.gis.measure import Distance 6 8 from django.contrib.gis.db.backend.postgis.management import postgis_version_tuple 7 from types import StringType, UnicodeType8 9 qn = connection.ops.quote_name 9 10 … … 63 64 # 'SQL-MM-centric' to conform with the ISO standard. Practically, this 64 65 # means that 'ST_' is prefixes geometry function names. 65 if MAJOR_VERSION > 1 or (MAJOR_VERSION == 1 and (MINOR_VERSION1 > 2 or (MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 2))): 66 GEOM_FUNC_PREFIX = 'ST_' 66 GEOM_FUNC_PREFIX = '' 67 if MAJOR_VERSION >= 1: 68 if (MINOR_VERSION1 > 2 or 69 (MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 2)): 70 GEOM_FUNC_PREFIX = 'ST_' 71 72 def get_func(func): return '%s%s' % (GEOM_FUNC_PREFIX, func) 73 74 # Functions used by the GeoManager & GeoQuerySet 75 ASKML = get_func('AsKML') 76 ASGML = get_func('AsGML') 77 DISTANCE = get_func('Distance') 78 GEOM_FROM_TEXT = get_func('GeomFromText') 79 GEOM_FROM_WKB = get_func('GeomFromWKB') 80 TRANSFORM = get_func('Transform') 81 82 # Special cases for union and KML methods. 83 if MINOR_VERSION1 < 3: 84 UNION = 'GeomUnion' 85 else: 86 UNION = 'ST_Union' 87 88 if MINOR_VERSION1 == 1: 89 ASKML = False 67 90 else: 68 GEOM_FUNC_PREFIX = ''91 raise NotImplementedError('PostGIS versions < 1.0 are not supported.') 69 92 70 93 # For PostGIS >= 1.2.2 the following lookup types will do a bounding box query 71 # first before calling the more computationally expensive GEOS routines (called72 # "inline index magic"):73 # 'touches', 'crosses', 'contains', 'intersects', 'within', 'overlaps', and74 # 'covers'.94 # first before calling the more computationally expensive GEOS routines (called 95 # "inline index magic"): 96 # 'touches', 'crosses', 'contains', 'intersects', 'within', 'overlaps', and 97 # 'covers'. 75 98 POSTGIS_GEOMETRY_FUNCTIONS = { 76 99 'equals' : 'Equals', … … 82 105 'contains' : 'Contains', 83 106 'intersects' : 'Intersects', 84 'relate' : ('Relate', str), 107 'relate' : ('Relate', basestring), 108 } 109 110 # Valid distance types and substitutions 111 dtypes = (Decimal, Distance, float, int) 112 DISTANCE_FUNCTIONS = { 113 'distance_gt' : ('>', dtypes), 114 'distance_gte' : ('>=', dtypes), 115 'distance_lt' : ('<', dtypes), 116 'distance_lte' : ('<=', dtypes), 85 117 } 86 118 87 119 if GEOM_FUNC_PREFIX == 'ST_': 88 120 # Adding the GEOM_FUNC_PREFIX to the lookup functions. 89 for lookup, f uncin POSTGIS_GEOMETRY_FUNCTIONS.items():90 if isinstance(f unc, tuple):91 POSTGIS_GEOMETRY_FUNCTIONS[lookup] = ( GEOM_FUNC_PREFIX + func[0], func[1])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]) 92 124 else: 93 POSTGIS_GEOMETRY_FUNCTIONS[lookup] = GEOM_FUNC_PREFIX + func125 POSTGIS_GEOMETRY_FUNCTIONS[lookup] = get_func(f) 94 126 95 127 # The ST_DWithin, ST_CoveredBy, and ST_Covers routines become available in 1.2.2+ 96 128 POSTGIS_GEOMETRY_FUNCTIONS.update( 97 {'dwithin' : ('ST_DWithin', float),129 {'dwithin' : ('ST_DWithin', dtypes), 98 130 'coveredby' : 'ST_CoveredBy', 99 131 'covers' : 'ST_Covers', 100 132 } 101 133 ) 134 135 POSTGIS_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS) 102 136 103 137 # Any other lookup types that do not require a mapping. … … 140 174 141 175 # Ensuring that a tuple _value_ was passed in from the user 142 if not isinstance(value, tuple) or len(value) != 2: 143 raise TypeError('2-element tuple required for `%s` lookup type.' % lookup_type) 176 if not isinstance(value, tuple): 177 raise TypeError('Tuple required for `%s` lookup type.' % lookup_type) 178 if len(value) != 2: 179 raise ValueError('2-element tuple required or `%s` lookup type.' % lookup_type) 144 180 145 181 # Ensuring the argument type matches what we expect. 146 182 if not isinstance(value[1], arg_type): 147 183 raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))) 148 149 return "%s(%s%s, %%s, %%s)" % (func, table_prefix, field_name) 184 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) 150 190 else: 151 191 # Returning the SQL necessary for the geometry function call. For example: 152 192 # ST_Contains("geoapp_country"."poly", ST_GeomFromWKB(..)) 153 193 return '%s(%s%s, %%s)' % (lookup_info, table_prefix, field_name) 154 194 155 195 # Handling 'isnull' lookup type 156 196 if lookup_type == 'isnull': … … 158 198 159 199 raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type)) 160 161 # Functions that we define manually.162 if MAJOR_VERSION == 1:163 if MINOR_VERSION1 == 3:164 # PostGIS versions 1.3.x165 ASKML = 'ST_AsKML'166 ASGML = 'ST_AsGML'167 GEOM_FROM_TEXT = 'ST_GeomFromText'168 GEOM_FROM_WKB = 'ST_GeomFromWKB'169 UNION = 'ST_Union'170 TRANSFORM = 'ST_Transform'171 elif MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 1:172 # PostGIS versions 1.2.x173 ASKML = 'AsKML'174 ASGML = 'AsGML'175 GEOM_FROM_TEXT = 'GeomFromText'176 GEOM_FROM_WKB = 'GeomFromWKB'177 UNION = 'GeomUnion'178 TRANSFORM = 'Transform'179 elif MINOR_VERSION1 == 1 and MINOR_VERSION2 >= 0:180 # PostGIS versions 1.1.x181 ASKML = False182 ASGML = 'AsGML'183 GEOM_FROM_TEXT = 'GeomFromText'184 GEOM_FROM_WKB = 'GeomFromWKB'185 TRANSFORM = 'Transform'186 UNION = 'GeomUnion'187 200 188 201 # Custom selection not needed for PostGIS since GEOS geometries may be django/branches/gis/django/contrib/gis/db/backend/util.py
r6596 r6886 7 7 self.where = where 8 8 self.params = params 9 10 def __str__(self): 11 return self.where[0] % tuple(self.params) 9 12 10 13 def get_srid(field, geom): django/branches/gis/django/contrib/gis/db/models/fields/__init__.py
r6508 r6886 1 from decimal import Decimal 1 2 from django.conf import settings 3 from django.db import connection 2 4 from django.contrib.gis.db.backend import GeoBackendField # these depend on the spatial database backend. 3 5 from django.contrib.gis.db.models.proxy import GeometryProxy 6 from django.contrib.gis.geos import GEOSException, GEOSGeometry 7 from django.contrib.gis.measure import Distance 4 8 from django.contrib.gis.oldforms import WKTField 5 from django.contrib.gis.geos import GEOSGeometry 9 10 # Attempting to get the spatial reference system. 11 try: 12 from django.contrib.gis.models import SpatialRefSys 13 except NotImplementedError: 14 SpatialRefSys = None 6 15 7 16 #TODO: Flesh out widgets; consider adding support for OGR Geometry proxies. … … 12 21 _geom = 'GEOMETRY' 13 22 14 def __init__(self, srid=4326, index=True, dim=2, **kwargs): 15 """The initialization function for geometry fields. Takes the following 23 def __init__(self, srid=4326, spatial_index=True, dim=2, **kwargs): 24 """ 25 The initialization function for geometry fields. Takes the following 16 26 as keyword arguments: 17 27 18 srid - The spatial reference system identifier. An OGC standard. 19 Defaults to 4326 (WGS84) 28 srid: 29 The spatial reference system identifier, an OGC standard. 30 Defaults to 4326 (WGS84). 20 31 21 index - Indicates whether to create a GiST index. Defaults to True. 22 Set this instead of 'db_index' for geographic fields since index 23 creation is different for geometry columns. 32 spatial_index: 33 Indicates whether to create a spatial index. Defaults to True. 34 Set this instead of 'db_index' for geographic fields since index 35 creation is different for geometry columns. 24 36 25 dim - The number of dimensions for this geometry. Defaults to 2. 37 dim: 38 The number of dimensions for this geometry. Defaults to 2. 26 39 """ 27 self._index = index 40 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. 28 50 self._srid = srid 51 if SpatialRefSys: 52 # This doesn't work when we actually use: SpatialRefSys.objects.get(srid=srid) 53 # 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. 56 cur = connection.cursor() 57 qn = connection.ops.quote_name 58 stmt = 'SELECT %(table)s.%(wkt_col)s FROM %(table)s WHERE (%(table)s.%(srid_col)s = %(srid)s)' 59 stmt = stmt % {'table' : qn(SpatialRefSys._meta.db_table), 60 'wkt_col' : qn(SpatialRefSys.wkt_col()), 61 'srid_col' : qn('srid'), 62 'srid' : srid, 63 } 64 cur.execute(stmt) 65 row = cur.fetchone() 66 self._unit, self._unit_name = SpatialRefSys.get_units(row[0]) 67 68 # Setting the dimension of the geometry field. 29 69 self._dim = dim 30 70 super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function 31 71 72 ### Routines specific to GeometryField ### 73 def get_distance(self, dist): 74 if isinstance(dist, Distance): 75 return getattr(dist, Distance.unit_attname(self._unit_name)) 76 elif isinstance(dist, (int, float, Decimal)): 77 # Assuming the distance is in the units of the field. 78 return dist 79 80 def get_geometry(self, value): 81 """ 82 Retrieves the geometry, setting the default SRID from the given 83 lookup parameters. 84 """ 85 if isinstance(value, tuple): 86 geom = value[0] 87 else: 88 geom = value 89 90 # When the input is not a GEOS geometry, attempt to construct one 91 # from the given string input. 92 if isinstance(geom, GEOSGeometry): 93 pass 94 elif isinstance(geom, basestring): 95 try: 96 geom = GEOSGeometry(geom) 97 except GEOSException: 98 raise ValueError('Could not create geometry from lookup value: %s' % str(value)) 99 else: 100 raise TypeError('Cannot use parameter of `%s` type as lookup parameter.' % type(value)) 101 102 # Assigning the SRID value. 103 geom.srid = self.get_srid(geom) 104 105 return geom 106 107 def get_srid(self, geom): 108 """ <
