Changeset 7641
- Timestamp:
- 06/15/08 14:48:57 (4 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/db/backend/base.py (added)
- django/branches/gis/django/contrib/gis/db/backend/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/mysql/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/oracle/adaptor.py (added)
- 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) (1 diff)
- django/branches/gis/django/contrib/gis/db/backend/postgis/field.py (modified) (1 diff)
- 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/models/fields/__init__.py (modified) (10 diffs)
- django/branches/gis/django/contrib/gis/db/models/manager.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/models/proxy.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/db/models/query.py (modified) (6 diffs)
- django/branches/gis/django/contrib/gis/db/models/sql/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/db/models/sql/query.py (modified) (8 diffs)
- django/branches/gis/django/contrib/gis/db/models/sql/where.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/measure.py (modified) (6 diffs)
- django/branches/gis/django/contrib/gis/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/tests/distapp/data.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/tests/distapp/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/tests/distapp/tests.py (modified) (10 diffs)
- django/branches/gis/django/contrib/gis/tests/geoapp/tests_mysql.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/tests/geoapp/tests.py (modified) (11 diffs)
- django/branches/gis/django/contrib/gis/tests/__init__.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/tests/layermap/models.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/tests/layermap/tests.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/tests/relatedapp/tests.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/db/backend/__init__.py
r7512 r7641 4 4 Specifically, this module will import the correct routines and modules 5 5 needed for GeoDjango to interface with the spatial database. 6 7 Some of the more important classes and routines from the spatial backend8 include:9 10 (1) `GeoBackEndField`, a base class needed for GeometryField.11 (2) `get_geo_where_clause`, a routine used by `GeoWhereNode`.12 (3) `GIS_TERMS`, a listing of all valid GeoDjango lookup types.13 (4) `SpatialBackend`, a container object for information specific to the14 spatial backend.15 6 """ 16 7 from django.conf import settings 17 from django.db.models.sql.query import QUERY_TERMS18 8 from django.contrib.gis.db.backend.util import gqn 19 20 # These routines (needed by GeoManager), default to False.21 ASGML, ASKML, DISTANCE, DISTANCE_SPHEROID, EXTENT, TRANSFORM, UNION, VERSION = tuple(False for i in range(8))22 23 # Lookup types in which the rest of the parameters are not24 # needed to be substitute in the WHERE SQL (e.g., the 'relate'25 # operation on Oracle does not need the mask substituted back26 # into the query SQL.).27 LIMITED_WHERE = []28 9 29 10 # Retrieving the necessary settings from the backend. 30 11 if settings.DATABASE_ENGINE == 'postgresql_psycopg2': 31 from django.contrib.gis.db.backend.postgis.adaptor import \ 32 PostGISAdaptor as GeoAdaptor 33 from django.contrib.gis.db.backend.postgis.field import \ 34 PostGISField as GeoBackendField 35 from django.contrib.gis.db.backend.postgis.creation import create_spatial_db 36 from django.contrib.gis.db.backend.postgis.query import \ 37 get_geo_where_clause, POSTGIS_TERMS as GIS_TERMS, \ 38 ASGML, ASKML, DISTANCE, DISTANCE_SPHEROID, DISTANCE_FUNCTIONS, \ 39 EXTENT, GEOM_SELECT, TRANSFORM, UNION, \ 40 MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 41 # PostGIS version info is needed to determine calling order of some 42 # stored procedures (e.g., AsGML()). 43 VERSION = (MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2) 44 SPATIAL_BACKEND = 'postgis' 12 from django.contrib.gis.db.backend.postgis import create_spatial_db, get_geo_where_clause, SpatialBackend 45 13 elif settings.DATABASE_ENGINE == 'oracle': 46 from django.contrib.gis.db.backend.adaptor import WKTAdaptor as GeoAdaptor 47 from django.contrib.gis.db.backend.oracle.field import \ 48 OracleSpatialField as GeoBackendField 49 from django.contrib.gis.db.backend.oracle.creation import create_spatial_db 50 from django.contrib.gis.db.backend.oracle.query import \ 51 get_geo_where_clause, ORACLE_SPATIAL_TERMS as GIS_TERMS, \ 52 ASGML, DISTANCE, DISTANCE_FUNCTIONS, GEOM_SELECT, TRANSFORM, UNION 53 SPATIAL_BACKEND = 'oracle' 54 LIMITED_WHERE = ['relate'] 14 from django.contrib.gis.db.backend.oracle import create_spatial_db, get_geo_where_clause, SpatialBackend 55 15 elif settings.DATABASE_ENGINE == 'mysql': 56 from django.contrib.gis.db.backend.adaptor import WKTAdaptor as GeoAdaptor 57 from django.contrib.gis.db.backend.mysql.field import \ 58 MySQLGeoField as GeoBackendField 59 from django.contrib.gis.db.backend.mysql.creation import create_spatial_db 60 from django.contrib.gis.db.backend.mysql.query import \ 61 get_geo_where_clause, MYSQL_GIS_TERMS as GIS_TERMS, GEOM_SELECT 62 DISTANCE_FUNCTIONS = {} 63 SPATIAL_BACKEND = 'mysql' 16 from django.contrib.gis.db.backend.mysql import create_spatial_db, get_geo_where_clause, SpatialBackend 64 17 else: 65 18 raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE) 66 67 class SpatialBackend(object):68 "A container for properties of the SpatialBackend."69 # Stored procedure names used by the `GeoManager`.70 as_kml = ASKML71 as_gml = ASGML72 distance = DISTANCE73 distance_spheroid = DISTANCE_SPHEROID74 extent = EXTENT75 name = SPATIAL_BACKEND76 select = GEOM_SELECT77 transform = TRANSFORM78 union = UNION79 80 # Version information, if defined.81 version = VERSION82 83 # All valid GIS lookup terms, and distance functions.84 gis_terms = GIS_TERMS85 distance_functions = DISTANCE_FUNCTIONS86 87 # Lookup types where additional WHERE parameters are excluded.88 limited_where = LIMITED_WHERE89 90 # Shortcut booleans.91 mysql = SPATIAL_BACKEND == 'mysql'92 oracle = SPATIAL_BACKEND == 'oracle'93 postgis = SPATIAL_BACKEND == 'postgis'94 95 # Class for the backend field.96 Field = GeoBackendField97 98 # Adaptor class used for quoting GEOS geometries in the database.99 Adaptor = GeoAdaptordjango/branches/gis/django/contrib/gis/db/backend/mysql/__init__.py
r7104 r7641 1 __all__ = ['create_spatial_db', 'get_geo_where_clause', 'SpatialBackend'] 1 2 3 from django.contrib.gis.db.backend.base import BaseSpatialBackend 4 from django.contrib.gis.db.backend.adaptor import WKTAdaptor 5 from django.contrib.gis.db.backend.mysql.creation import create_spatial_db 6 from django.contrib.gis.db.backend.mysql.field import MySQLGeoField 7 from django.contrib.gis.db.backend.mysql.query import * 8 9 SpatialBackend = BaseSpatialBackend(name='mysql', mysql=True, 10 gis_terms=MYSQL_GIS_TERMS, 11 select=GEOM_SELECT, 12 Adaptor=WKTAdaptor, 13 Field=MySQLGeoField) django/branches/gis/django/contrib/gis/db/backend/oracle/__init__.py
r7104 r7641 1 __all__ = ['create_spatial_db', 'get_geo_where_clause', 'SpatialBackend'] 2 3 from django.contrib.gis.db.backend.base import BaseSpatialBackend 4 from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor 5 from django.contrib.gis.db.backend.oracle.creation import create_spatial_db 6 from django.contrib.gis.db.backend.oracle.field import OracleSpatialField 7 from django.contrib.gis.db.backend.oracle.query import * 8 9 SpatialBackend = BaseSpatialBackend(name='oracle', oracle=True, 10 area=AREA, 11 centroid=CENTROID, 12 difference=DIFFERENCE, 13 distance=DISTANCE, 14 distance_functions=DISTANCE_FUNCTIONS, 15 gis_terms=ORACLE_SPATIAL_TERMS, 16 gml=ASGML, 17 intersection=INTERSECTION, 18 length=LENGTH, 19 limited_where = {'relate' : None}, 20 num_geom=NUM_GEOM, 21 num_points=NUM_POINTS, 22 perimeter=LENGTH, 23 point_on_surface=POINT_ON_SURFACE, 24 select=GEOM_SELECT, 25 sym_difference=SYM_DIFFERENCE, 26 transform=TRANSFORM, 27 unionagg=UNIONAGG, 28 union=UNION, 29 Adaptor=OracleSpatialAdaptor, 30 Field=OracleSpatialField, 31 ) django/branches/gis/django/contrib/gis/db/backend/oracle/query.py
r7483 r7641 16 16 17 17 # The GML, distance, transform, and union procedures. 18 AREA = 'SDO_GEOM.SDO_AREA' 18 19 ASGML = 'SDO_UTIL.TO_GMLGEOMETRY' 20 CENTROID = 'SDO_GEOM.SDO_CENTROID' 21 DIFFERENCE = 'SDO_GEOM.SDO_DIFFERENCE' 19 22 DISTANCE = 'SDO_GEOM.SDO_DISTANCE' 23 EXTENT = 'SDO_AGGR_MBR' 24 INTERSECTION = 'SDO_GEOM.SDO_INTERSECTION' 25 LENGTH = 'SDO_GEOM.SDO_LENGTH' 26 NUM_GEOM = 'SDO_UTIL.GETNUMELEM' 27 NUM_POINTS = 'SDO_UTIL.GETNUMVERTICES' 28 POINT_ON_SURFACE = 'SDO_GEOM.SDO_POINTONSURFACE' 29 SYM_DIFFERENCE = 'SDO_GEOM.SDO_XOR' 20 30 TRANSFORM = 'SDO_CS.TRANSFORM' 21 UNION = 'SDO_AGGR_UNION' 31 UNION = 'SDO_GEOM.SDO_UNION' 32 UNIONAGG = 'SDO_AGGR_UNION' 22 33 23 34 # We want to get SDO Geometries as WKT because it is much easier to django/branches/gis/django/contrib/gis/db/backend/postgis/field.py
r7104 r7641 1 1 from django.db import connection 2 2 from django.db.models.fields import Field # Django base Field class 3 from django.contrib.gis.geos import GEOSGeometry4 3 from django.contrib.gis.db.backend.util import gqn 5 4 from django.contrib.gis.db.backend.postgis.query import TRANSFORM django/branches/gis/django/contrib/gis/db/backend/postgis/__init__.py
r7104 r7641 1 __all__ = ['create_spatial_db', 'get_geo_where_clause', 'SpatialBackend'] 2 3 from django.contrib.gis.db.backend.base import BaseSpatialBackend 4 from django.contrib.gis.db.backend.postgis.adaptor import PostGISAdaptor 5 from django.contrib.gis.db.backend.postgis.creation import create_spatial_db 6 from django.contrib.gis.db.backend.postgis.field import PostGISField 7 from django.contrib.gis.db.backend.postgis.query import * 8 9 SpatialBackend = BaseSpatialBackend(name='postgis', postgis=True, 10 area=AREA, 11 centroid=CENTROID, 12 difference=DIFFERENCE, 13 distance=DISTANCE, 14 distance_functions=DISTANCE_FUNCTIONS, 15 distance_sphere=DISTANCE_SPHERE, 16 distance_spheroid=DISTANCE_SPHEROID, 17 envelope=ENVELOPE, 18 extent=EXTENT, 19 gis_terms=POSTGIS_TERMS, 20 gml=ASGML, 21 intersection=INTERSECTION, 22 kml=ASKML, 23 length=LENGTH, 24 length_spheroid=LENGTH_SPHEROID, 25 make_line=MAKE_LINE, 26 mem_size=MEM_SIZE, 27 num_geom=NUM_GEOM, 28 num_points=NUM_POINTS, 29 perimeter=PERIMETER, 30 point_on_surface=POINT_ON_SURFACE, 31 scale=SCALE, 32 select=GEOM_SELECT, 33 svg=ASSVG, 34 sym_difference=SYM_DIFFERENCE, 35 transform=TRANSFORM, 36 translate=TRANSLATE, 37 union=UNION, 38 unionagg=UNIONAGG, 39 version=(MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2), 40 Adaptor=PostGISAdaptor, 41 Field=PostGISField, 42 ) django/branches/gis/django/contrib/gis/db/backend/postgis/query.py
r7456 r7641 22 22 # Versions of PostGIS >= 1.2.2 changed their naming convention to be 23 23 # 'SQL-MM-centric' to conform with the ISO standard. Practically, this 24 # means that 'ST_' isprefixes geometry function names.24 # means that 'ST_' prefixes geometry function names. 25 25 GEOM_FUNC_PREFIX = '' 26 26 if MAJOR_VERSION >= 1: … … 31 31 def get_func(func): return '%s%s' % (GEOM_FUNC_PREFIX, func) 32 32 33 # Custom selection not needed for PostGIS since GEOS geometries may be33 # Custom selection not needed for PostGIS because GEOS geometries are 34 34 # instantiated directly from the HEXEWKB returned by default. If 35 35 # WKT is needed for some reason in the future, this value may be changed, 36 # 'AsText(%s)'36 # e.g,, 'AsText(%s)'. 37 37 GEOM_SELECT = None 38 38 39 39 # Functions used by the GeoManager & GeoQuerySet 40 AREA = get_func('Area') 40 41 ASKML = get_func('AsKML') 41 42 ASGML = get_func('AsGML') 43 ASSVG = get_func('AsSVG') 44 CENTROID = get_func('Centroid') 45 DIFFERENCE = get_func('Difference') 42 46 DISTANCE = get_func('Distance') 47 DISTANCE_SPHERE = get_func('distance_sphere') 43 48 DISTANCE_SPHEROID = get_func('distance_spheroid') 49 ENVELOPE = get_func('Envelope') 44 50 EXTENT = get_func('extent') 45 51 GEOM_FROM_TEXT = get_func('GeomFromText') 46 52 GEOM_FROM_WKB = get_func('GeomFromWKB') 53 INTERSECTION = get_func('Intersection') 54 LENGTH = get_func('Length') 55 LENGTH_SPHEROID = get_func('length_spheroid') 56 MAKE_LINE = get_func('MakeLine') 57 MEM_SIZE = get_func('mem_size') 58 NUM_GEOM = get_func('NumGeometries') 59 NUM_POINTS = get_func('npoints') 60 PERIMETER = get_func('Perimeter') 61 POINT_ON_SURFACE = get_func('PointOnSurface') 62 SCALE = get_func('Scale') 63 SYM_DIFFERENCE = get_func('SymDifference') 47 64 TRANSFORM = get_func('Transform') 65 TRANSLATE = get_func('Translate') 48 66 49 67 # Special cases for union and KML methods. 50 68 if MINOR_VERSION1 < 3: 51 UNION = 'GeomUnion' 69 UNIONAGG = 'GeomUnion' 70 UNION = 'Union' 52 71 else: 72 UNIONAGG = 'ST_Union' 53 73 UNION = 'ST_Union' 54 74 … … 81 101 operator=operator, result='%%s') 82 102 103 class PostGISSpheroidDistance(PostGISFunction): 104 "For PostGIS spherical distance operations (using the spheroid)." 105 dist_func = 'distance_spheroid' 106 def __init__(self, operator): 107 # An extra parameter in `end_subst` is needed for the spheroid string. 108 super(PostGISSpheroidDistance, self).__init__(self.dist_func, 109 beg_subst='%s(%s, %%s, %%s', 110 end_subst=') %s %s', 111 operator=operator, result='%%s') 112 83 113 class PostGISSphereDistance(PostGISFunction): 84 114 "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', 115 dist_func = 'distance_sphere' 116 def __init__(self, operator): 117 super(PostGISSphereDistance, self).__init__(self.dist_func, end_subst=') %s %s', 91 118 operator=operator, result='%%s') 92 119 93 120 class PostGISRelate(PostGISFunctionParam): 94 121 "For PostGIS Relate(<geom>, <pattern>) calls." … … 165 192 def get_dist_ops(operator): 166 193 "Returns operations for both regular and spherical distances." 167 return (PostGISDistance(operator), PostGISSphereDistance(operator) )194 return (PostGISDistance(operator), PostGISSphereDistance(operator), PostGISSpheroidDistance(operator)) 168 195 DISTANCE_FUNCTIONS = { 169 196 'distance_gt' : (get_dist_ops('>'), dtypes), … … 193 220 POSTGIS_TERMS += MISC_TERMS # Adding any other miscellaneous terms (e.g., 'isnull') 194 221 POSTGIS_TERMS = tuple(POSTGIS_TERMS) # Making immutable 222 223 # For checking tuple parameters -- not very pretty but gets job done. 224 def exactly_two(val): return val == 2 225 def two_to_three(val): return val >= 2 and val <=3 226 def num_params(lookup_type, val): 227 if lookup_type in DISTANCE_FUNCTIONS and lookup_type != 'dwithin': return two_to_three(val) 228 else: return exactly_two(val) 195 229 196 230 #### The `get_geo_where_clause` function for PostGIS. #### … … 217 251 if not isinstance(value, (tuple, list)): 218 252 raise TypeError('Tuple required for `%s` lookup type.' % lookup_type) 219 if len(value) != 2: 220 raise ValueError('2-element tuple required or `%s` lookup type.' % lookup_type) 253 # Number of valid tuple parameters depends on the lookup type. 254 nparams = len(value) 255 if not num_params(lookup_type, nparams): 256 raise ValueError('Incorrect number of parameters given for `%s` lookup type.' % lookup_type) 221 257 222 258 # Ensuring the argument type matches what we expect. … … 235 271 if value[0].geom_typeid != 0: 236 272 raise TypeError('PostGIS geometry distance parameter is required to be of type Point.') 237 op = op[1] 273 # Setting up the geodetic operation appropriately. 274 if nparams == 3 and value[2] == 'spheroid': op = op[2] 275 else: op = op[1] 238 276 else: 239 277 op = op[0] django/branches/gis/django/contrib/gis/db/models/fields/__init__.py
r7482 r7641 4 4 # GeometryProxy, GEOS, Distance, and oldforms imports. 5 5 from django.contrib.gis.db.models.proxy import GeometryProxy 6 from django.contrib.gis.geos import GEOSException, GEOSGeometry7 6 from django.contrib.gis.measure import Distance 8 7 from django.contrib.gis.oldforms import WKTField 9 8 10 # Attempting to get the spatial reference system. 11 try: 12 from django.contrib.gis.models import SpatialRefSys 13 except ImportError: 14 SpatialRefSys = None 9 # The `get_srid_info` function gets SRID information from the spatial 10 # reference system table w/o using the ORM. 11 from django.contrib.gis.models import get_srid_info 15 12 16 13 #TODO: Flesh out widgets; consider adding support for OGR Geometry proxies. … … 48 45 # easily available in the field instance for distance queries. 49 46 self._srid = srid 50 if SpatialRefSys: 51 # Getting the spatial reference WKT associated with the SRID from the 52 # `spatial_ref_sys` (or equivalent) spatial database table. 53 # 54 # The following doesn't work: SpatialRefSys.objects.get(srid=srid) 55 # Why? `syncdb` fails to recognize installed geographic models when there's 56 # an ORM query instantiated within a model field. 57 cur = connection.cursor() 58 qn = connection.ops.quote_name 59 stmt = 'SELECT %(table)s.%(wkt_col)s FROM %(table)s WHERE (%(table)s.%(srid_col)s = %(srid)s)' 60 stmt = stmt % {'table' : qn(SpatialRefSys._meta.db_table), 61 'wkt_col' : qn(SpatialRefSys.wkt_col()), 62 'srid_col' : qn('srid'), 63 'srid' : srid, 64 } 65 cur.execute(stmt) 66 srs_wkt = cur.fetchone()[0] 67 68 # Getting metadata associated with the spatial reference system identifier. 69 # Specifically, getting the unit information and spheroid information 70 # (both required for distance queries). 71 self._unit, self._unit_name = SpatialRefSys.get_units(srs_wkt) 72 self._spheroid = SpatialRefSys.get_spheroid(srs_wkt) 47 self._unit, self._unit_name, self._spheroid = get_srid_info(srid) 73 48 74 49 # Setting the dimension of the geometry field. … … 80 55 @property 81 56 def geodetic(self): 57 """ 58 Returns true if this field's SRID corresponds with a coordinate 59 system that uses non-projected units (e.g., latitude/longitude). 60 """ 82 61 return self._unit_name in self.geodetic_units 83 62 84 def get_distance(self, dist , lookup_type):63 def get_distance(self, dist_val, lookup_type): 85 64 """ 86 65 Returns a distance number in units of the field. For example, if … … 88 67 then 1000 would be returned. 89 68 """ 90 postgis = SpatialBackend.name == 'postgis' 69 # Getting the distance parameter and any options. 70 if len(dist_val) == 1: dist, option = dist_val[0], None 71 else: dist, option = dist_val 72 91 73 if isinstance(dist, Distance): 92 74 if self.geodetic: 93 75 # Won't allow Distance objects w/DWithin lookups on PostGIS. 94 if postgis and lookup_type == 'dwithin':76 if SpatialBackend.postgis and lookup_type == 'dwithin': 95 77 raise TypeError('Only numeric values of degree units are allowed on geographic DWithin queries.') 96 78 # Spherical distance calculation parameter should be in meters. … … 101 83 # Assuming the distance is in the units of the field. 102 84 dist_param = dist 103 104 # Sphereical distance query; returning meters. 105 if postgis and self.geodetic and lookup_type != 'dwithin': 85 86 if SpatialBackend.postgis and self.geodetic and lookup_type != 'dwithin' and option == 'spheroid': 87 # On PostGIS, by default `ST_distance_sphere` is used; but if the 88 # accuracy of `ST_distance_spheroid` is needed than the spheroid 89 # needs to be passed to the SQL stored procedure. 106 90 return [gqn(self._spheroid), dist_param] 107 91 else: … … 120 104 # When the input is not a GEOS geometry, attempt to construct one 121 105 # from the given string input. 122 if isinstance(geom, GEOSGeometry):106 if isinstance(geom, SpatialBackend.Geometry): 123 107 pass 124 108 elif isinstance(geom, basestring): 125 109 try: 126 geom = GEOSGeometry(geom)127 except GEOSException:110 geom = SpatialBackend.Geometry(geom) 111 except SpatialBackend.GeometryException: 128 112 raise ValueError('Could not create geometry from lookup value: %s' % str(value)) 129 113 else: … … 149 133 super(GeometryField, self).contribute_to_class(cls, name) 150 134 151 # Setup for lazy-instantiated G EOSGeometry object.152 setattr(cls, self.attname, GeometryProxy( GEOSGeometry, self))135 # Setup for lazy-instantiated Geometry object. 136 setattr(cls, self.attname, GeometryProxy(SpatialBackend.Geometry, self)) 153 137 154 138 def get_db_prep_lookup(self, lookup_type, value): … … 167 151 168 152 # Getting the WHERE clause list and the associated params list. The params 169 # list is populated with the Adaptor wrapping the G EOSGeometry for the153 # list is populated with the Adaptor wrapping the Geometry for the 170 154 # backend. The WHERE clause list contains the placeholder for the adaptor 171 155 # (e.g. any transformation SQL). … … 176 160 if lookup_type in SpatialBackend.distance_functions: 177 161 # Getting the distance parameter in the units of the field. 178 where += self.get_distance(value[1 ], lookup_type)162 where += self.get_distance(value[1:], lookup_type) 179 163 elif lookup_type in SpatialBackend.limited_where: 180 164 pass … … 188 172 def get_db_prep_save(self, value): 189 173 "Prepares the value for saving in the database." 190 if isinstance(value, GEOSGeometry):174 if isinstance(value, SpatialBackend.Geometry): 191 175 return SpatialBackend.Adaptor(value) 192 176 elif value is None: 193 177 return None 194 178 else: 195 raise TypeError('Geometry Proxy should only return G EOSGeometry objects or None.')179 raise TypeError('Geometry Proxy should only return Geometry objects or None.') 196 180 197 181 def get_manipulator_field_objs(self): 198 "Using the WKTField ( defined above) to be our manipulator."182 "Using the WKTField (oldforms) to be our manipulator." 199 183 return [WKTField] 200 184 django/branches/gis/django/contrib/gis/db/models/manager.py
r7028 r7641 8 8 return GeoQuerySet(model=self.model) 9 9 10 def area(self, *args, **kwargs): 11 return self.get_query_set().area(*args, **kwargs) 12 13 def centroid(self, *args, **kwargs): 14 return self.get_query_set().centroid(*args, **kwargs) 15 16 def difference(self, *args, **kwargs): 17 return self.get_query_set().difference(*args, **kwargs) 18 10 19 def distance(self, *args, **kwargs): 11 20 return self.get_query_set().distance(*args, **kwargs) 21 22 def envelope(self, *args, **kwargs): 23 return self.get_query_set().envelope(*args, **kwargs) 12 24 13 25 def extent(self, *args, **kwargs): … … 17 29 return self.get_query_set().gml(*args, **kwargs) 18 30 31 def intersection(self, *args, **kwargs): 32 return self.get_query_set().intersection(*args, **kwargs) 33 19 34 def kml(self, *args, **kwargs): 20 35 return self.get_query_set().kml(*args, **kwargs) 36 37 def length(self, *args, **kwargs): 38 return self.get_query_set().length(*args, **kwargs) 39 40 def make_line(self, *args, **kwargs): 41 return self.get_query_set().make_line(*args, **kwargs) 42 43 def mem_size(self, *args, **kwargs): 44 return self.get_query_set().mem_size(*args, **kwargs) 45 46 def num_geom(self, *args, **kwargs): 47 return self.get_query_set().num_geom(*args, **kwargs) 48 49 def num_points(self, *args, **kwargs): 50 return self.get_query_set().num_points(*args, **kwargs) 51 52 def perimeter(self, *args, **kwargs): 53 return self.get_query_set().perimeter(*args, **kwargs) 54 55 def point_on_surface(self, *args, **kwargs): 56 return self.get_query_set().point_on_surface(*args, **kwargs) 57 58 def scale(self, *args, **kwargs): 59 return self.get_query_set().scale(*args, **kwargs) 60 61 def svg(self, *args, **kwargs): 62 return self.get_query_set().svg(*args, **kwargs) 63 64 def sym_difference(self, *args, **kwargs): 65 return self.get_query_set().sym_difference(*args, **kwargs) 21 66 22 67 def transform(self, *args, **kwargs): 23 68 return self.get_query_set().transform(*args, **kwargs) 24 69 70 def translate(self, *args, **kwargs): 71 return self.get_query_set().translate(*args, **kwargs) 72 25 73 def union(self, *args, **kwargs): 26 74 return self.get_query_set().union(*args, **kwargs) 75 76 def unionagg(self, *args, **kwargs): 77 return self.get_query_set().unionagg(*args, **kwargs) django/branches/gis/django/contrib/gis/db/models/proxy.py
r6508 r7641 1 1 """ 2 2 The GeometryProxy object, allows for lazy-geometries. The proxy uses 3 Python descriptors for instantiating and setting GEOSGeometry objects4 corresponding to geographic model fields.3 Python descriptors for instantiating and setting Geometry objects 4 corresponding to geographic model fields. 5 5 6 6 Thanks to Robert Coup for providing this functionality (see #4322). … … 32 32 geom = None 33 33 else: 34 # Otherwise, a G EOSGeometry object is built using the field's contents,35 # and the model's corresponding attribute is set.34 # Otherwise, a Geometry object is built using the field's contents, 35 # and the model's corresponding attribute is set. 36 36 geom = self._klass(geom_value) 37 37 setattr(obj, self._field.attname, geom) django/branches/gis/django/contrib/gis/db/models/query.py
r7512 r7641 1 from itertools import izip2 1 from django.core.exceptions import ImproperlyConfigured 3 2 from django.db import connection … … 6 5 from django.contrib.gis.db.backend import SpatialBackend 7 6 from django.contrib.gis.db.models.fields import GeometryField, PointField 8 from django.contrib.gis.db.models.sql import GeoQuery, GeoWhereNode 9 from django.contrib.gis.geos import GEOSGeometry, Point 7 from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery, GeoWhereNode 8 from django.contrib.gis.measure import Area, Distance 9 from django.contrib.gis.models import get_srid_info 10 10 qn = connection.ops.quote_name 11 11 … … 29 29 self.query = query or GeoQuery(self.model, connection) 30 30 31 def distance(self, *args, **kwargs): 31 def area(self, tolerance=0.05, **kwargs): 32 """ 33 Returns the area of the geographic field in an `area` attribute on 34 each element of this GeoQuerySet. 35 """ 36 # Peforming setup here rather than in `_spatial_attribute` so that 37 # we can get the units for `AreaField`. 38 procedure_args, geo_field = self._spatial_setup('area', field_name=kwargs.get('field_name', None)) 39 s = {'procedure_args' : procedure_args, 40 'geo_field' : geo_field, 41 'setup' : False, 42 } 43 if SpatialBackend.oracle: 44 s['procedure_fmt'] = '%(geo_col)s,%(tolerance)s' 45 s['procedure_args']['tolerance'] = tolerance 46 s['select_field'] = AreaField('sq_m') # Oracle returns area in units of meters. 47 elif SpatialBackend.postgis: 48 if not geo_field.geodetic: 49 # Getting the area units of the geographic field. 50 s['select_field'] = AreaField(Area.unit_attname(geo_field._unit_name)) 51 else: 52 # TODO: Do we want to support raw number areas for geodetic fields? 53 raise Exception('Area on geodetic coordinate systems not supported.') 54 return self._spatial_attribute('area', s, **kwargs) 55 56 def centroid(self, **kwargs): 57 """ 58 Returns the centroid of the geographic field in a `centroid` 59 attribute on each element of this GeoQuerySet. 60 """ 61 return self._geom_attribute('centroid', **kwargs) 62 63 def difference(self, geom, **kwargs): 64 """ 65 Returns the spatial difference of the geographic field in a `difference` 66 attribute on each element of this GeoQuerySet. 67 """ 68 return self._geomset_attribute('difference', geom, **kwargs) 69 70 def distance(self, geom, **kwargs): 32 71 """ 33 72 Returns the distance from the given geographic field name to the 34 73 given geometry in a `distance` attribute on each element of the 35 74 GeoQuerySet. 36 """ 37 DISTANCE = SpatialBackend.distance 38 if not DISTANCE: 39 raise ImproperlyConfigured('Distance() stored proecedure not available.') 40 41 # Getting the geometry field and GEOSGeometry object to base distance 42 # calculations from. 43 nargs = len(args) 44 if nargs == 1: 45 field_name = None 46 geom = args[0] 47 elif nargs == 2: 48 field_name, geom = args 49 else: 50 raise ValueError('Maximum two arguments allowed for `distance` aggregate.') 51 52 # Getting the GeometryField and quoted column. 53 geo_field = self.query._geo_field(field_name) 54 if not geo_field: 55 raise TypeError('Distance output only available on GeometryFields.') 56 geo_col = self.query._field_column(geo_field) 57 58 # Using the field's get_db_prep_lookup() to get any needed 59 # transformation SQL -- we pass in a 'dummy' `contains` 60 # `distance_lte` lookup type. 61 where, params = geo_field.get_db_prep_lookup('distance_lte', (geom, 0)) 62 if SpatialBackend.oracle: 63 # The `tolerance` keyword may be used for Oracle; the tolerance is 64 # in meters -- a default of 5 centimeters is used. 65 tolerance = kwargs.get('tolerance', 0.05) 66 dist_select = {'distance' : '%s(%s, %s, %s)' % (DISTANCE, geo_col, where[0], tolerance)} 67 else: 68 if len(where) == 3: 69 # Spherical distance calculation was requested (b/c spheroid 70 # parameter was attached) However, the PostGIS ST_distance_spheroid() 71 # procedure may only do queries from point columns to point geometries 72 # some error checking is required. 73 if not isinstance(geo_field, PointField): 74 raise TypeError('Spherical distance calculation only supported on PointFields.') 75 if not isinstance(GEOSGeometry(buffer(params[0].wkb)), Point): 76 raise TypeError('Spherical distance calculation only supported with Point Geometry parameters') 77 78 # Call to distance_spheroid() requires the spheroid as well. 79 dist_sql = '%s(%s, %s, %s)' % (SpatialBackend.distance_spheroid, geo_col, where[0], where[1]) 80 else: 81 dist_sql = '%s(%s, %s)' % (DISTANCE, geo_col, where[0]) 82 dist_select = {'distance' : dist_sql} 83 return self.extra(select=dist_select, select_params=params) 84 85 def extent(self, field_name=None): 75 76 Keyword Arguments: 77 `spheroid` => If the geometry field is geodetic and PostGIS is 78 the spatial database, then the more accurate 79 spheroid calculation will be used instead of the 80 quicker sphere calculation. 81 82 `tolerance` => Used only for Oracle. The tolerance is 83 in meters -- a default of 5 centimeters (0.05) 84 is used. 85 """ 86 return self._distance_attribute('distance', geom, **kwargs) 87 88 def envelope(self, **kwargs): 89 """ 90 Returns a Geometry representing the bounding box of the 91 Geometry field in an `envelope` attribute on each element of 92 the GeoQuerySet. 93 """ 94 return self._geom_attribute('envelope', **kwargs) 95 96 def extent(self, **kwargs): 86 97 """ 87 98 Returns the extent (aggregate) of the features in the GeoQuerySet. The 88 99 extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax). 89 100 """ 90 EXTENT = SpatialBackend.extent 91 if not EXTENT: 92 raise ImproperlyConfigured('Extent stored procedure not available.') 93 94 # Getting the GeometryField and quoted co
