Django

Code

Changeset 7028

Show
Ignore:
Timestamp:
01/21/08 19:22:27 (6 months ago)
Author:
jbronn
Message:

gis: Added the extent method to GeoQuerySet; moved various spatial-backend settings into the SpatialBackend container class.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis/django/contrib/gis/db/backend/__init__.py

    r6919 r7028  
    66  
    77 (1) GeoBackEndField, a base class needed for GeometryField. 
    8  (2) GeometryProxy, for lazy-instantiated geometries from the  
    9      database output. 
    10  (3) GIS_TERMS, a list of acceptable geographic lookup types for  
     8 (2) GIS_TERMS, a list of acceptable geographic lookup types for  
    119     the backend. 
    12  (4) The `parse_lookup` function, used for spatial SQL construction by 
     10 (3) The `parse_lookup` function, used for spatial SQL construction by 
    1311     the GeoQuerySet. 
    14  (5) The `create_spatial_db`, and `get_geo_where_clause` 
     12 (4) The `create_spatial_db`, and `get_geo_where_clause` 
    1513     routines (needed by `parse_lookup`). 
    16  
    17  Currently only PostGIS is supported, but someday backends will be added for 
    18  additional spatial databases (e.g., Oracle, DB2). 
     14 (5) The `SpatialBackend` object, which contains information specific 
     15     to the spatial backend. 
    1916""" 
    2017from types import StringType, UnicodeType 
     
    2724 
    2825# These routines (needed by GeoManager), default to False. 
    29 ASGML, ASKML, DISTANCE, TRANSFORM, UNION, VERSION = (False, False, False, False, False, False) 
     26ASGML, ASKML, DISTANCE, EXTENT, TRANSFORM, UNION, VERSION = (False, False, False, False, False, False, False) 
    3027 
    3128if settings.DATABASE_ENGINE == 'postgresql_psycopg2': 
     
    3532        PostGISField as GeoBackendField, POSTGIS_TERMS as GIS_TERMS, \ 
    3633        create_spatial_db, get_geo_where_clause, \ 
    37         ASGML, ASKML, DISTANCE, GEOM_SELECT, TRANSFORM, UNION, \ 
     34        ASGML, ASKML, DISTANCE, EXTENT, GEOM_SELECT, TRANSFORM, UNION, \ 
    3835        MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 
    3936    VERSION = (MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2) 
     
    5552else: 
    5653    raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE) 
     54 
     55class SpatialBackend(object): 
     56    "A container for properties of the Spatial Backend." 
     57    as_kml = ASKML 
     58    as_gml = ASGML 
     59    distance = DISTANCE 
     60    extent = EXTENT 
     61    name = SPATIAL_BACKEND 
     62    select = GEOM_SELECT 
     63    transform = TRANSFORM 
     64    union = UNION 
     65    version = VERSION 
    5766 
    5867####    query.py overloaded functions    #### 
  • django/branches/gis/django/contrib/gis/db/backend/postgis/__init__.py

    r6886 r7028  
    77    get_geo_where_clause, GEOM_FUNC_PREFIX, POSTGIS_TERMS, \ 
    88    MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2, \ 
    9     ASKML, ASGML, DISTANCE, GEOM_FROM_TEXT, UNION, TRANSFORM, GEOM_SELECT 
     9    ASKML, ASGML, DISTANCE, EXTENT, GEOM_FROM_TEXT, UNION, TRANSFORM, GEOM_SELECT 
  • django/branches/gis/django/contrib/gis/db/backend/postgis/query.py

    r6919 r7028  
    4141    ASGML = get_func('AsGML') 
    4242    DISTANCE = get_func('Distance') 
     43    EXTENT = get_func('extent') 
    4344    GEOM_FROM_TEXT = get_func('GeomFromText') 
    4445    GEOM_FROM_WKB = get_func('GeomFromWKB') 
  • django/branches/gis/django/contrib/gis/db/models/manager.py

    r6886 r7028  
    1111        return self.get_query_set().distance(*args, **kwargs) 
    1212 
     13    def extent(self, *args, **kwargs): 
     14        return self.get_query_set().extent(*args, **kwargs) 
     15 
    1316    def gml(self, *args, **kwargs): 
    1417        return self.get_query_set().gml(*args, **kwargs) 
  • django/branches/gis/django/contrib/gis/db/models/query.py

    r6966 r7028  
    77from django.contrib.gis.db.models.fields import GeometryField 
    88# parse_lookup depends on the spatial database backend. 
    9 from django.contrib.gis.db.backend import parse_lookup, \ 
    10     ASGML, ASKML, DISTANCE, GEOM_SELECT, SPATIAL_BACKEND, TRANSFORM, UNION, VERSION 
     9from django.contrib.gis.db.backend import parse_lookup, SpatialBackend 
    1110from django.contrib.gis.geos import GEOSGeometry 
    1211 
    1312# Shortcut booleans for determining the backend. 
    14 oracle = SPATIAL_BACKEND == 'oracle' 
    15 postgis = SPATIAL_BACKEND == 'postgis' 
     13oracle = SpatialBackend.name == 'oracle' 
     14postgis = SpatialBackend.name == 'postgis' 
    1615 
    1716class GeoQ(Q): 
     
    2423class GeoQuerySet(QuerySet): 
    2524    "Geographical-enabled QuerySet object." 
    26      
     25         
    2726    #### Overloaded QuerySet Routines #### 
    2827    def __init__(self, model=None): 
     
    3837        # If GEOM_SELECT is defined in the backend, then it will be used 
    3938        # for the selection format of the geometry column. 
    40         if GEOM_SELECT
     39        if SpatialBackend.select
    4140            # Transformed geometries in Oracle use EWKT so that the SRID 
    4241            # on the transformed lazy geometries is set correctly). 
    43             self._geo_fmt = GEOM_SELECT 
     42            self._geo_fmt = SpatialBackend.select 
    4443        else: 
    4544            self._geo_fmt = '%s' 
     
    260259        GeoQuerySet. 
    261260        """ 
     261        DISTANCE = SpatialBackend.distance 
    262262        if not DISTANCE: 
    263263            raise ImproperlyConfigured('Distance() stored proecedure not available.') 
     
    300300        return self.extra(select=dist_select) 
    301301 
     302    def extent(self, field_name=None): 
     303        """ 
     304        Returns the extent (aggregate) of the features in the GeoQuerySet.  The 
     305        extent will be returned as a 4-tuple, consisting of (xmin, ymin, xmax, ymax). 
     306        """ 
     307        EXTENT = SpatialBackend.extent 
     308        if not EXTENT: 
     309            raise ImproperlyConfigured('Extent stored procedure not available.') 
     310 
     311        if not field_name: 
     312            field_name = self._get_geofield() 
     313 
     314        field_col = self._geo_column(field_name) 
     315        if not field_col: 
     316            raise TypeError('Extent information only available on GeometryFields.') 
     317 
     318        # Getting the SQL for the query. 
     319        try: 
     320            select, sql, params = self._get_sql_clause() 
     321        except EmptyResultSet: 
     322            return None 
     323         
     324        # Constructing the query that will select the extent. 
     325        extent_sql = ('SELECT %s(%s)' % (EXTENT, field_col)) + sql 
     326 
     327        # Getting a cursor, executing the query, and extracting the returned 
     328        # value from the extent function. 
     329        cursor = connection.cursor() 
     330        cursor.execute(extent_sql, params) 
     331        box = cursor.fetchone()[0] 
     332 
     333        if box:  
     334            # TODO: Parsing of BOX3D, Oracle support (patches welcome!) 
     335            #  Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)";  
     336            #  parsing out and returning as a 4-tuple. 
     337            ll, ur = box[4:-1].split(',') 
     338            xmin, ymin = map(float, ll.split()) 
     339            xmax, ymax = map(float, ur.split()) 
     340            return (xmin, ymin, xmax, ymax) 
     341        else:  
     342            return None 
     343 
    302344    def gml(self, field_name=None, precision=8, version=2): 
    303345        """ 
     
    306348        """ 
    307349        # Is GML output supported? 
     350        ASGML = SpatialBackend.as_gml 
    308351        if not ASGML: 
    309352            raise ImproperlyConfigured('AsGML() stored procedure not available.') 
     
    323366            # PostGIS AsGML() aggregate function parameter order depends on the 
    324367            # version -- uggh. 
    325             major, minor1, minor2 = VERSION 
     368            major, minor1, minor2 = SpatialBackend.version 
    326369            if major >= 1 and (minor1 > 3 or (minor1 == 3 and minor2 > 1)): 
    327370                gml_select = {'gml':'%s(%s,%s,%s)' % (ASGML, version, field_col, precision)} 
     
    338381        """ 
    339382        # Is KML output supported? 
     383        ASKML = SpatialBackend.as_kml 
    340384        if not ASKML: 
    341385            raise ImproperlyConfigured('AsKML() stored procedure not available.') 
     
    376420        # Setting the key for the field's column with the custom SELECT SQL to  
    377421        # override the geometry column returned from the database. 
     422        TRANSFORM = SpatialBackend.transform 
    378423        if oracle: 
    379424            custom_sel = '%s(%s, %s)' % (TRANSFORM, col, srid) 
     
    392437        """ 
    393438        # Making sure backend supports the Union stored procedure 
     439        UNION = SpatialBackend.union 
    394440        if not UNION: 
    395441            raise ImproperlyConfigured('Union stored procedure not available.') 
  • django/branches/gis/django/contrib/gis/tests/geoapp/tests.py

    r6979 r7028  
    172172            self.assertAlmostEqual(ptown.x, p.point.x, prec) 
    173173            self.assertAlmostEqual(ptown.y, p.point.y, prec) 
     174 
     175    @no_oracle # Most likely can do this in Oracle, however, it is not yet implemented (patches welcome!) 
     176    def test05_extent(self): 
     177        "Testing the extent() GeoManager method." 
     178        # Reference query: 
     179        # `SELECT ST_extent(point) FROM geoapp_city WHERE (name='Houston' or name='Dallas');` 
     180        #   =>  BOX(-96.8016128540039 29.7633724212646,-95.3631439208984 32.7820587158203) 
     181        expected = (-96.8016128540039, 29.7633724212646, -95.3631439208984, 32.782058715820) 
     182 
     183        qs = City.objects.filter(name__in=('Houston', 'Dallas')) 
     184        extent = qs.extent() 
     185 
     186        for val, exp in zip(extent, expected): 
     187            self.assertAlmostEqual(exp, val, 8) 
    174188 
    175189    def test09_disjoint(self): 
  • django/branches/gis/django/contrib/gis/utils/layermapping.py

    r7024 r7028  
    113113from django.core.exceptions import ObjectDoesNotExist 
    114114from django.contrib.gis.db.models.fields import GeometryField 
    115 from django.contrib.gis.db.backend import SPATIAL_BACKEND 
     115from django.contrib.gis.db.backend import SpatialBackend 
    116116from django.contrib.gis.gdal import CoordTransform, DataSource, \ 
    117117    OGRException, OGRGeometry, OGRGeomType, SpatialReference 
     
    507507        try: 
    508508            db_table = self.model._meta.db_table 
    509             if SPATIAL_BACKEND == 'oracle': db_table = db_table.upper() 
     509            if SpatialBackend.name == 'oracle': db_table = db_table.upper() 
    510510            gc_kwargs = {GeometryColumns.table_name_col() : db_table} 
    511511            return GeometryColumns.objects.get(**gc_kwargs)