Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merged the gis branch into trunk.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8219 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 79e68c225b926302ebb29c808dda8afa49856f5c 1 parent d0f57e7
Justin Bronn authored August 05, 2008

Showing 163 changed files with 14,939 additions and 0 deletions. Show diff stats Hide diff stats

  1. BIN  django/contrib/admin/media/img/gis/move_vertex_off.png
  2. BIN  django/contrib/admin/media/img/gis/move_vertex_on.png
  3. 12  django/contrib/gis/admin/__init__.py
  4. 128  django/contrib/gis/admin/options.py
  5. 92  django/contrib/gis/admin/widgets.py
  6. 18  django/contrib/gis/db/backend/__init__.py
  7. 14  django/contrib/gis/db/backend/adaptor.py
  8. 29  django/contrib/gis/db/backend/base.py
  9. 13  django/contrib/gis/db/backend/mysql/__init__.py
  10. 5  django/contrib/gis/db/backend/mysql/creation.py
  11. 53  django/contrib/gis/db/backend/mysql/field.py
  12. 59  django/contrib/gis/db/backend/mysql/query.py
  13. 31  django/contrib/gis/db/backend/oracle/__init__.py
  14. 5  django/contrib/gis/db/backend/oracle/adaptor.py
  15. 8  django/contrib/gis/db/backend/oracle/creation.py
  16. 103  django/contrib/gis/db/backend/oracle/field.py
  17. 49  django/contrib/gis/db/backend/oracle/models.py
  18. 154  django/contrib/gis/db/backend/oracle/query.py
  19. 42  django/contrib/gis/db/backend/postgis/__init__.py
  20. 33  django/contrib/gis/db/backend/postgis/adaptor.py
  21. 224  django/contrib/gis/db/backend/postgis/creation.py
  22. 95  django/contrib/gis/db/backend/postgis/field.py
  23. 54  django/contrib/gis/db/backend/postgis/management.py
  24. 58  django/contrib/gis/db/backend/postgis/models.py
  25. 287  django/contrib/gis/db/backend/postgis/query.py
  26. 52  django/contrib/gis/db/backend/util.py
  27. 17  django/contrib/gis/db/models/__init__.py
  28. 214  django/contrib/gis/db/models/fields/__init__.py
  29. 82  django/contrib/gis/db/models/manager.py
  30. 11  django/contrib/gis/db/models/mixin.py
  31. 62  django/contrib/gis/db/models/proxy.py
  32. 617  django/contrib/gis/db/models/query.py
  33. 2  django/contrib/gis/db/models/sql/__init__.py
  34. 327  django/contrib/gis/db/models/sql/query.py
  35. 64  django/contrib/gis/db/models/sql/where.py
  36. 1  django/contrib/gis/forms/__init__.py
  37. 37  django/contrib/gis/forms/fields.py
  38. 28  django/contrib/gis/gdal/LICENSE
  39. 51  django/contrib/gis/gdal/__init__.py
  40. 138  django/contrib/gis/gdal/datasource.py
  41. 66  django/contrib/gis/gdal/driver.py
  42. 134  django/contrib/gis/gdal/envelope.py
  43. 41  django/contrib/gis/gdal/error.py
  44. 115  django/contrib/gis/gdal/feature.py
  45. 179  django/contrib/gis/gdal/field.py
  46. 643  django/contrib/gis/gdal/geometries.py
  47. 73  django/contrib/gis/gdal/geomtype.py
  48. 187  django/contrib/gis/gdal/layer.py
  49. 83  django/contrib/gis/gdal/libgdal.py
  50. 68  django/contrib/gis/gdal/prototypes/ds.py
  51. 125  django/contrib/gis/gdal/prototypes/errcheck.py
  52. 116  django/contrib/gis/gdal/prototypes/generation.py
  53. 109  django/contrib/gis/gdal/prototypes/geom.py
  54. 71  django/contrib/gis/gdal/prototypes/srs.py
  55. 360  django/contrib/gis/gdal/srs.py
  56. 27  django/contrib/gis/geos/LICENSE
  57. 69  django/contrib/gis/geos/__init__.py
  58. 608  django/contrib/gis/geos/base.py
  59. 105  django/contrib/gis/geos/collections.py
  60. 164  django/contrib/gis/geos/coordseq.py
  61. 20  django/contrib/gis/geos/error.py
  62. 391  django/contrib/gis/geos/geometries.py
  63. 126  django/contrib/gis/geos/libgeos.py
  64. 33  django/contrib/gis/geos/prototypes/__init__.py
  65. 82  django/contrib/gis/geos/prototypes/coordseq.py
  66. 76  django/contrib/gis/geos/prototypes/errcheck.py
  67. 111  django/contrib/gis/geos/prototypes/geom.py
  68. 27  django/contrib/gis/geos/prototypes/misc.py
  69. 43  django/contrib/gis/geos/prototypes/predicates.py
  70. 35  django/contrib/gis/geos/prototypes/topology.py
  71. 15  django/contrib/gis/management/base.py
  72. 190  django/contrib/gis/management/commands/inspectdb.py
  73. 119  django/contrib/gis/management/commands/ogrinspect.py
  74. 61  django/contrib/gis/maps/google/__init__.py
  75. 138  django/contrib/gis/maps/google/gmap.py
  76. 220  django/contrib/gis/maps/google/overlays.py
  77. 164  django/contrib/gis/maps/google/zoom.py
  78. 329  django/contrib/gis/measure.py
  79. 284  django/contrib/gis/models.py
  80. 29  django/contrib/gis/oldforms/__init__.py
  81. 12  django/contrib/gis/shortcuts.py
  82. 55  django/contrib/gis/sitemaps.py
  83. 37  django/contrib/gis/templates/gis/admin/openlayers.html
  84. 157  django/contrib/gis/templates/gis/admin/openlayers.js
  85. 2  django/contrib/gis/templates/gis/admin/osm.html
  86. 2  django/contrib/gis/templates/gis/admin/osm.js
  87. 34  django/contrib/gis/templates/gis/google/js/google-map.js
  88. 6  django/contrib/gis/templates/gis/kml/base.kml
  89. 8  django/contrib/gis/templates/gis/kml/placemarks.kml
  90. 148  django/contrib/gis/tests/__init__.py
  91. BIN  django/contrib/gis/tests/data/test_point/test_point.dbf
  92. 1  django/contrib/gis/tests/data/test_point/test_point.prj
  93. BIN  django/contrib/gis/tests/data/test_point/test_point.shp
  94. BIN  django/contrib/gis/tests/data/test_point/test_point.shx
  95. BIN  django/contrib/gis/tests/data/test_poly/test_poly.dbf
  96. 1  django/contrib/gis/tests/data/test_poly/test_poly.prj
  97. BIN  django/contrib/gis/tests/data/test_poly/test_poly.shp
  98. BIN  django/contrib/gis/tests/data/test_poly/test_poly.shx
  99. 4  django/contrib/gis/tests/data/test_vrt/test_vrt.csv
  100. 7  django/contrib/gis/tests/data/test_vrt/test_vrt.vrt
  101. 33  django/contrib/gis/tests/distapp/data.py
  102. 42  django/contrib/gis/tests/distapp/models.py
  103. 296  django/contrib/gis/tests/distapp/tests.py
  104. 33  django/contrib/gis/tests/geoapp/models.py
  105. 8  django/contrib/gis/tests/geoapp/sql/city.mysql.sql
  106. 8  django/contrib/gis/tests/geoapp/sql/city.oracle.sql
  107. 8  django/contrib/gis/tests/geoapp/sql/city.postgresql_psycopg2.sql
  108. 1  django/contrib/gis/tests/geoapp/sql/co.wkt
  109. 4  django/contrib/gis/tests/geoapp/sql/country.mysql.sql
  110. 4  django/contrib/gis/tests/geoapp/sql/country.postgresql_psycopg2.sql
  111. 1  django/contrib/gis/tests/geoapp/sql/ks.wkt
  112. 1  django/contrib/gis/tests/geoapp/sql/nz.wkt
  113. 5  django/contrib/gis/tests/geoapp/sql/state.mysql.sql
  114. 5  django/contrib/gis/tests/geoapp/sql/state.postgresql_psycopg2.sql
  115. 1  django/contrib/gis/tests/geoapp/sql/tx.wkt
  116. 564  django/contrib/gis/tests/geoapp/tests.py
  117. 179  django/contrib/gis/tests/geoapp/tests_mysql.py
  118. 157  django/contrib/gis/tests/geometries.py
  119. BIN  django/contrib/gis/tests/layermap/cities/cities.dbf
  120. 1  django/contrib/gis/tests/layermap/cities/cities.prj
  121. BIN  django/contrib/gis/tests/layermap/cities/cities.shp
  122. BIN  django/contrib/gis/tests/layermap/cities/cities.shx
  123. BIN  django/contrib/gis/tests/layermap/counties/counties.dbf
  124. BIN  django/contrib/gis/tests/layermap/counties/counties.shp
  125. BIN  django/contrib/gis/tests/layermap/counties/counties.shx
  126. BIN  django/contrib/gis/tests/layermap/interstates/interstates.dbf
  127. 1  django/contrib/gis/tests/layermap/interstates/interstates.prj
  128. BIN  django/contrib/gis/tests/layermap/interstates/interstates.shp
  129. BIN  django/contrib/gis/tests/layermap/interstates/interstates.shx
  130. 52  django/contrib/gis/tests/layermap/models.py
  131. 249  django/contrib/gis/tests/layermap/tests.py
  132. 12  django/contrib/gis/tests/relatedapp/models.py
  133. 98  django/contrib/gis/tests/relatedapp/tests.py
  134. 1  django/contrib/gis/tests/relatedapp/tests_mysql.py
  135. 28  django/contrib/gis/tests/test_gdal.py
  136. 40  django/contrib/gis/tests/test_gdal_driver.py
  137. 181  django/contrib/gis/tests/test_gdal_ds.py
  138. 45  django/contrib/gis/tests/test_gdal_envelope.py
  139. 403  django/contrib/gis/tests/test_gdal_geom.py
  140. 169  django/contrib/gis/tests/test_gdal_srs.py
  141. 104  django/contrib/gis/tests/test_geoip.py
  142. 775  django/contrib/gis/tests/test_geos.py
  143. 333  django/contrib/gis/tests/test_measure.py
  144. 90  django/contrib/gis/tests/test_spatialrefsys.py
  145. 22  django/contrib/gis/tests/utils.py
  146. 25  django/contrib/gis/utils/__init__.py
  147. 344  django/contrib/gis/utils/geoip.py
  148. 677  django/contrib/gis/utils/layermapping.py
  149. 53  django/contrib/gis/utils/ogrinfo.py
  150. 225  django/contrib/gis/utils/ogrinspect.py
  151. 27  django/contrib/gis/utils/srs.py
  152. 55  django/contrib/gis/utils/wkt.py
  153. 0  gis/__init__.py b/django/contrib/gis/__init__.py
  154. 0  gis/db/__init__.py b/django/contrib/gis/db/__init__.py
  155. 0  gis/gdal/prototypes/__init__.py b/django/contrib/gis/gdal/prototypes/__init__.py
  156. 0  gis/management/__init__.py b/django/contrib/gis/management/__init__.py
  157. 0  gis/management/commands/__init__.py b/django/contrib/gis/management/commands/__init__.py
  158. 0  gis/maps/__init__.py b/django/contrib/gis/maps/__init__.py
  159. 0  gis/maps/openlayers/__init__.py b/django/contrib/gis/maps/openlayers/__init__.py
  160. 0  gis/tests/distapp/__init__.py b/django/contrib/gis/tests/distapp/__init__.py
  161. 0  gis/tests/geoapp/__init__.py b/django/contrib/gis/tests/geoapp/__init__.py
  162. 0  gis/tests/layermap/__init__.py b/django/contrib/gis/tests/layermap/__init__.py
  163. 0  gis/tests/relatedapp/__init__.py b/django/contrib/gis/tests/relatedapp/__init__.py
BIN  django/contrib/admin/media/img/gis/move_vertex_off.png
BIN  django/contrib/admin/media/img/gis/move_vertex_on.png
12  django/contrib/gis/admin/__init__.py
... ...
@@ -0,0 +1,12 @@
  1
+# Getting the normal admin routines, classes, and `site` instance.
  2
+from django.contrib.admin import autodiscover, site, StackedInline, TabularInline, HORIZONTAL, VERTICAL
  3
+
  4
+# Geographic admin options classes and widgets.
  5
+from django.contrib.gis.admin.options import GeoModelAdmin
  6
+from django.contrib.gis.admin.widgets import OpenLayersWidget
  7
+
  8
+try:
  9
+    from django.contrib.gis.admin.options import OSMGeoAdmin
  10
+    HAS_OSM = True
  11
+except ImportError:
  12
+    HAS_OSM = False
128  django/contrib/gis/admin/options.py
... ...
@@ -0,0 +1,128 @@
  1
+from django.conf import settings
  2
+from django.contrib.admin import ModelAdmin
  3
+from django.contrib.gis.admin.widgets import OpenLayersWidget
  4
+from django.contrib.gis.gdal import OGRGeomType
  5
+from django.contrib.gis.db import models
  6
+
  7
+class GeoModelAdmin(ModelAdmin):
  8
+    """
  9
+    The administration options class for Geographic models. Map settings
  10
+    may be overloaded from their defaults to create custom maps.
  11
+    """
  12
+    # The default map settings that may be overloaded -- still subject
  13
+    # to API changes.
  14
+    default_lon = 0
  15
+    default_lat = 0
  16
+    default_zoom = 4
  17
+    display_wkt = False
  18
+    display_srid = False
  19
+    extra_js = []
  20
+    num_zoom = 18
  21
+    max_zoom = False
  22
+    min_zoom = False
  23
+    units = False
  24
+    max_resolution = False
  25
+    max_extent = False
  26
+    modifiable = True
  27
+    mouse_position = True
  28
+    scale_text = True
  29
+    layerswitcher = True
  30
+    scrollable = True
  31
+    admin_media_prefix = settings.ADMIN_MEDIA_PREFIX
  32
+    map_width = 600
  33
+    map_height = 400
  34
+    map_srid = 4326
  35
+    map_template = 'gis/admin/openlayers.html'
  36
+    openlayers_url = 'http://openlayers.org/api/2.6/OpenLayers.js'
  37
+    wms_url = 'http://labs.metacarta.com/wms/vmap0'
  38
+    wms_layer = 'basic'
  39
+    wms_name = 'OpenLayers WMS'
  40
+    debug = False
  41
+    widget = OpenLayersWidget
  42
+
  43
+    def _media(self):
  44
+        "Injects OpenLayers JavaScript into the admin."
  45
+        media = super(GeoModelAdmin, self)._media()
  46
+        media.add_js([self.openlayers_url])
  47
+        media.add_js(self.extra_js)
  48
+        return media
  49
+    media = property(_media)
  50
+
  51
+    def formfield_for_dbfield(self, db_field, **kwargs):
  52
+        """
  53
+        Overloaded from ModelAdmin so that an OpenLayersWidget is used
  54
+        for viewing/editing GeometryFields.
  55
+        """
  56
+        if isinstance(db_field, models.GeometryField):
  57
+            # Setting the widget with the newly defined widget.
  58
+            kwargs['widget'] = self.get_map_widget(db_field)
  59
+            return db_field.formfield(**kwargs)
  60
+        else:
  61
+            return super(GeoModelAdmin, self).formfield_for_dbfield(db_field, **kwargs)
  62
+
  63
+    def get_map_widget(self, db_field):
  64
+        """
  65
+        Returns a subclass of the OpenLayersWidget (or whatever was specified
  66
+        in the `widget` attribute) using the settings from the attributes set 
  67
+        in this class.
  68
+        """
  69
+        is_collection = db_field._geom in ('MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON', 'GEOMETRYCOLLECTION')
  70
+        if is_collection:
  71
+            if db_field._geom == 'GEOMETRYCOLLECTION': collection_type = 'Any'
  72
+            else: collection_type = OGRGeomType(db_field._geom.replace('MULTI', ''))
  73
+        else:
  74
+            collection_type = 'None'
  75
+
  76
+        class OLMap(self.widget):
  77
+            template = self.map_template
  78
+            geom_type = db_field._geom
  79
+            params = {'admin_media_prefix' : self.admin_media_prefix,
  80
+                      'default_lon' : self.default_lon,
  81
+                      'default_lat' : self.default_lat,
  82
+                      'default_zoom' : self.default_zoom,
  83
+                      'display_wkt' : self.debug or self.display_wkt,
  84
+                      'geom_type' : OGRGeomType(db_field._geom),
  85
+                      'field_name' : db_field.name,
  86
+                      'is_collection' : is_collection,
  87
+                      'scrollable' : self.scrollable,
  88
+                      'layerswitcher' : self.layerswitcher,
  89
+                      'collection_type' : collection_type,
  90
+                      'is_linestring' : db_field._geom in ('LINESTRING', 'MULTILINESTRING'),
  91
+                      'is_polygon' : db_field._geom in ('POLYGON', 'MULTIPOLYGON'),
  92
+                      'is_point' : db_field._geom in ('POINT', 'MULTIPOINT'),
  93
+                      'num_zoom' : self.num_zoom,
  94
+                      'max_zoom' : self.max_zoom,
  95
+                      'min_zoom' : self.min_zoom,
  96
+                      'units' : self.units, #likely shoud get from object
  97
+                      'max_resolution' : self.max_resolution,
  98
+                      'max_extent' : self.max_extent,
  99
+                      'modifiable' : self.modifiable,
  100
+                      'mouse_position' : self.mouse_position,
  101
+                      'scale_text' : self.scale_text,
  102
+                      'map_width' : self.map_width,
  103
+                      'map_height' : self.map_height,
  104
+                      'srid' : self.map_srid,
  105
+                      'display_srid' : self.display_srid,
  106
+                      'wms_url' : self.wms_url,
  107
+                      'wms_layer' : self.wms_layer,
  108
+                      'wms_name' : self.wms_name,
  109
+                      'debug' : self.debug,
  110
+                      }
  111
+        return OLMap
  112
+
  113
+# Using the Beta OSM in the admin requires the following:
  114
+#  (1) The Google Maps Mercator projection needs to be added
  115
+#      to your `spatial_ref_sys` table.  You'll need at least GDAL 1.5:
  116
+#      >>> from django.contrib.gis.gdal import SpatialReference
  117
+#      >>> from django.contrib.gis.utils import add_postgis_srs
  118
+#      >>> add_postgis_srs(SpatialReference(900913)) # Adding the Google Projection 
  119
+from django.contrib.gis import gdal
  120
+if gdal.HAS_GDAL:
  121
+    class OSMGeoAdmin(GeoModelAdmin):
  122
+        map_template = 'gis/admin/osm.html'
  123
+        extra_js = ['http://openstreetmap.org/openlayers/OpenStreetMap.js']
  124
+        num_zoom = 20
  125
+        map_srid = 900913
  126
+        max_extent = '-20037508,-20037508,20037508,20037508'
  127
+        max_resolution = 156543.0339
  128
+        units = 'm'
92  django/contrib/gis/admin/widgets.py
... ...
@@ -0,0 +1,92 @@
  1
+from django.contrib.gis.gdal import OGRException
  2
+from django.contrib.gis.geos import GEOSGeometry, GEOSException
  3
+from django.forms.widgets import Textarea
  4
+from django.template.loader import render_to_string
  5
+
  6
+class OpenLayersWidget(Textarea):
  7
+    """
  8
+    Renders an OpenLayers map using the WKT of the geometry.
  9
+    """
  10
+    def render(self, name, value, attrs=None):
  11
+        # Update the template parameters with any attributes passed in.
  12
+        if attrs: self.params.update(attrs)
  13
+
  14
+        # Defaulting the WKT value to a blank string -- this
  15
+        # will be tested in the JavaScript and the appropriate
  16
+        # interfaace will be constructed.
  17
+        self.params['wkt'] = ''
  18
+
  19
+        # If a string reaches here (via a validation error on another
  20
+        # field) then just reconstruct the Geometry.
  21
+        if isinstance(value, basestring):
  22
+            try:
  23
+                value = GEOSGeometry(value)
  24
+            except (GEOSException, ValueError):
  25
+                value = None
  26
+
  27
+        if value and value.geom_type.upper() != self.geom_type:
  28
+            value = None
  29
+
  30
+        # Constructing the dictionary of the map options.
  31
+        self.params['map_options'] = self.map_options()
  32
+
  33
+        # Constructing the JavaScript module name using the ID of
  34
+        # the GeometryField (passed in via the `attrs` keyword).
  35
+        self.params['module'] = 'geodjango_%s' % self.params['field_name']
  36
+
  37
+        if value:
  38
+            # Transforming the geometry to the projection used on the
  39
+            # OpenLayers map.
  40
+            srid = self.params['srid']
  41
+            if value.srid != srid: 
  42
+                try:
  43
+                    value.transform(srid)
  44
+                    wkt = value.wkt
  45
+                except OGRException:
  46
+                    wkt = ''
  47
+            else:
  48
+                wkt = value.wkt
  49
+               
  50
+            # Setting the parameter WKT with that of the transformed
  51
+            # geometry.
  52
+            self.params['wkt'] = wkt
  53
+
  54
+        return render_to_string(self.template, self.params)
  55
+    
  56
+    def map_options(self):
  57
+        "Builds the map options hash for the OpenLayers template."
  58
+
  59
+        # JavaScript construction utilities for the Bounds and Projection.
  60
+        def ol_bounds(extent):
  61
+            return 'new OpenLayers.Bounds(%s)' % str(extent)
  62
+        def ol_projection(srid):
  63
+            return 'new OpenLayers.Projection("EPSG:%s")' % srid
  64
+
  65
+        # An array of the parameter name, the name of their OpenLayers
  66
+        # counterpart, and the type of variable they are.
  67
+        map_types = [('srid', 'projection', 'srid'), 
  68
+                     ('display_srid', 'displayProjection', 'srid'), 
  69
+                     ('units', 'units', str),
  70
+                     ('max_resolution', 'maxResolution', float),
  71
+                     ('max_extent', 'maxExtent', 'bounds'),
  72
+                     ('num_zoom', 'numZoomLevels', int),
  73
+                     ('max_zoom', 'maxZoomLevels', int),
  74
+                     ('min_zoom', 'minZoomLevel', int),
  75
+                     ]
  76
+
  77
+        # Building the map options hash.
  78
+        map_options = {}
  79
+        for param_name, js_name, option_type in map_types:
  80
+            if self.params.get(param_name, False):
  81
+                if option_type == 'srid':
  82
+                    value = ol_projection(self.params[param_name])
  83
+                elif option_type == 'bounds':
  84
+                    value = ol_bounds(self.params[param_name])
  85
+                elif option_type in (float, int):
  86
+                    value = self.params[param_name]
  87
+                elif option_type in (str,):
  88
+                    value = '"%s"' % self.params[param_name]
  89
+                else:
  90
+                    raise TypeError
  91
+                map_options[js_name] = value
  92
+        return map_options
18  django/contrib/gis/db/backend/__init__.py
... ...
@@ -0,0 +1,18 @@
  1
+"""
  2
+ This module provides the backend for spatial SQL construction with Django.
  3
+
  4
+ Specifically, this module will import the correct routines and modules
  5
+ needed for GeoDjango to interface with the spatial database.
  6
+"""
  7
+from django.conf import settings
  8
+from django.contrib.gis.db.backend.util import gqn
  9
+
  10
+# Retrieving the necessary settings from the backend.
  11
+if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
  12
+    from django.contrib.gis.db.backend.postgis import create_spatial_db, get_geo_where_clause, SpatialBackend
  13
+elif settings.DATABASE_ENGINE == 'oracle':
  14
+    from django.contrib.gis.db.backend.oracle import create_spatial_db, get_geo_where_clause, SpatialBackend
  15
+elif settings.DATABASE_ENGINE == 'mysql':
  16
+    from django.contrib.gis.db.backend.mysql import create_spatial_db, get_geo_where_clause, SpatialBackend
  17
+else:
  18
+    raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE)
14  django/contrib/gis/db/backend/adaptor.py
... ...
@@ -0,0 +1,14 @@
  1
+class WKTAdaptor(object):
  2
+    """
  3
+    This provides an adaptor for Geometries sent to the
  4
+    MySQL and Oracle database backends.
  5
+    """
  6
+    def __init__(self, geom):
  7
+        self.wkt = geom.wkt
  8
+        self.srid = geom.srid
  9
+
  10
+    def __eq__(self, other):
  11
+        return self.wkt == other.wkt and self.srid == other.srid 
  12
+
  13
+    def __str__(self):
  14
+        return self.wkt
29  django/contrib/gis/db/backend/base.py
... ...
@@ -0,0 +1,29 @@
  1
+"""
  2
+ This module holds the base `SpatialBackend` object, which is
  3
+ instantiated by each spatial backend with the features it has.
  4
+"""
  5
+# TODO: Create a `Geometry` protocol and allow user to use
  6
+# different Geometry objects -- for now we just use GEOSGeometry.
  7
+from django.contrib.gis.geos import GEOSGeometry, GEOSException
  8
+
  9
+class BaseSpatialBackend(object):
  10
+    Geometry = GEOSGeometry
  11
+    GeometryException = GEOSException
  12
+
  13
+    def __init__(self, **kwargs):
  14
+        kwargs.setdefault('distance_functions', {})
  15
+        kwargs.setdefault('limited_where', {})
  16
+        for k, v in kwargs.iteritems(): setattr(self, k, v)
  17
+ 
  18
+    def __getattr__(self, name):
  19
+        """
  20
+        All attributes of the spatial backend return False by default.
  21
+        """
  22
+        try:
  23
+            return self.__dict__[name]
  24
+        except KeyError:
  25
+            return False
  26
+            
  27
+        
  28
+        
  29
+    
13  django/contrib/gis/db/backend/mysql/__init__.py
... ...
@@ -0,0 +1,13 @@
  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.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)
5  django/contrib/gis/db/backend/mysql/creation.py
... ...
@@ -0,0 +1,5 @@
  1
+from django.test.utils import create_test_db
  2
+
  3
+def create_spatial_db(test=True, verbosity=1, autoclobber=False):
  4
+    if not test: raise NotImplementedError('This uses `create_test_db` from test/utils.py')
  5
+    create_test_db(verbosity, autoclobber)
53  django/contrib/gis/db/backend/mysql/field.py
... ...
@@ -0,0 +1,53 @@
  1
+from django.db import connection
  2
+from django.db.models.fields import Field # Django base Field class
  3
+from django.contrib.gis.db.backend.mysql.query import GEOM_FROM_TEXT
  4
+
  5
+# Quotename & geographic quotename, respectively.
  6
+qn = connection.ops.quote_name
  7
+
  8
+class MySQLGeoField(Field):
  9
+    """
  10
+    The backend-specific geographic field for MySQL.
  11
+    """
  12
+
  13
+    def _geom_index(self, style, db_table):
  14
+        """
  15
+        Creates a spatial index for the geometry column.  If MyISAM tables are
  16
+        used an R-Tree index is created, otherwise a B-Tree index is created. 
  17
+        Thus, for best spatial performance, you should use MyISAM tables
  18
+        (which do not support transactions).  For more information, see Ch. 
  19
+        16.6.1 of the MySQL 5.0 documentation.
  20
+        """
  21
+
  22
+        # Getting the index name.
  23
+        idx_name = '%s_%s_id' % (db_table, self.column)
  24
+        
  25
+        sql = style.SQL_KEYWORD('CREATE SPATIAL INDEX ') + \
  26
+              style.SQL_TABLE(qn(idx_name)) + \
  27
+              style.SQL_KEYWORD(' ON ') + \
  28
+              style.SQL_TABLE(qn(db_table)) + '(' + \
  29
+              style.SQL_FIELD(qn(self.column)) + ');'
  30
+        return sql
  31
+
  32
+    def _post_create_sql(self, style, db_table):
  33
+        """
  34
+        Returns SQL that will be executed after the model has been
  35
+        created.
  36
+        """
  37
+        # Getting the geometric index for this Geometry column.
  38
+        if self._index:
  39
+            return (self._geom_index(style, db_table),)
  40
+        else:
  41
+            return ()
  42
+
  43
+    def db_type(self):
  44
+        "The OpenGIS name is returned for the MySQL database column type."
  45
+        return self._geom
  46
+
  47
+    def get_placeholder(self, value):
  48
+        """
  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.
  52
+        """
  53
+        return '%s(%%s)' % GEOM_FROM_TEXT
59  django/contrib/gis/db/backend/mysql/query.py
... ...
@@ -0,0 +1,59 @@
  1
+"""
  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.
  9
+"""
  10
+from django.db import connection
  11
+qn = connection.ops.quote_name
  12
+
  13
+# To ease implementation, WKT is passed to/from MySQL.
  14
+GEOM_FROM_TEXT = 'GeomFromText'
  15
+GEOM_FROM_WKB = 'GeomFromWKB'
  16
+GEOM_SELECT = 'AsText(%s)'
  17
+
  18
+# WARNING: MySQL is NOT compliant w/the OpenGIS specification and
  19
+# _every_ one of these lookup types is on the _bounding box_ only.
  20
+MYSQL_GIS_FUNCTIONS = {
  21
+    'bbcontains' : 'MBRContains', # For consistency w/PostGIS API
  22
+    'bboverlaps' : 'MBROverlaps', # .. ..
  23
+    'contained' : 'MBRWithin',    # .. ..
  24
+    'contains' : 'MBRContains',
  25
+    'disjoint' : 'MBRDisjoint',
  26
+    'equals' : 'MBREqual',
  27
+    'exact' : 'MBREqual',
  28
+    'intersects' : 'MBRIntersects',
  29
+    'overlaps' : 'MBROverlaps',
  30
+    'same_as' : 'MBREqual',
  31
+    'touches' : 'MBRTouches',
  32
+    'within' : 'MBRWithin',
  33
+    }
  34
+
  35
+# This lookup type does not require a mapping.
  36
+MISC_TERMS = ['isnull']
  37
+
  38
+# Assacceptable lookup types for Oracle spatial.
  39
+MYSQL_GIS_TERMS  = MYSQL_GIS_FUNCTIONS.keys()
  40
+MYSQL_GIS_TERMS += MISC_TERMS
  41
+MYSQL_GIS_TERMS = dict((term, None) for term in MYSQL_GIS_TERMS) # Making dictionary 
  42
+
  43
+def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
  44
+    "Returns the SQL WHERE clause for use in MySQL spatial SQL construction."
  45
+    # Getting the quoted field as `geo_col`.
  46
+    geo_col = '%s.%s' % (qn(table_alias), qn(name))
  47
+
  48
+    # See if a MySQL Geometry function matches the lookup type next
  49
+    lookup_info = MYSQL_GIS_FUNCTIONS.get(lookup_type, False)
  50
+    if lookup_info:
  51
+        return "%s(%s, %%s)" % (lookup_info, geo_col)
  52
+    
  53
+    # Handling 'isnull' lookup type
  54
+    # TODO: Is this needed because MySQL cannot handle NULL
  55
+    # geometries in its spatial indices.
  56
+    if lookup_type == 'isnull':
  57
+        return "%s IS %sNULL" % (geo_col, (not geo_annot.value and 'NOT ' or ''))
  58
+
  59
+    raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
31  django/contrib/gis/db/backend/oracle/__init__.py
... ...
@@ -0,0 +1,31 @@
  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
+                                    )
5  django/contrib/gis/db/backend/oracle/adaptor.py
... ...
@@ -0,0 +1,5 @@
  1
+from cx_Oracle import CLOB
  2
+from django.contrib.gis.db.backend.adaptor import WKTAdaptor
  3
+
  4
+class OracleSpatialAdaptor(WKTAdaptor):
  5
+    input_size = CLOB
8  django/contrib/gis/db/backend/oracle/creation.py
... ...
@@ -0,0 +1,8 @@
  1
+from django.db.backends.oracle.creation import create_test_db
  2
+
  3
+def create_spatial_db(test=True, verbosity=1, autoclobber=False):
  4
+    "A wrapper over the Oracle `create_test_db` routine."
  5
+    if not test: raise NotImplementedError('This uses `create_test_db` from db/backends/oracle/creation.py')
  6
+    from django.conf import settings
  7
+    from django.db import connection
  8
+    create_test_db(settings, connection, verbosity, autoclobber)
103  django/contrib/gis/db/backend/oracle/field.py
... ...
@@ -0,0 +1,103 @@
  1
+from django.db import connection
  2
+from django.db.backends.util import truncate_name
  3
+from django.db.models.fields import Field # Django base Field class
  4
+from django.contrib.gis.db.backend.util import gqn
  5
+from django.contrib.gis.db.backend.oracle.query import TRANSFORM
  6
+
  7
+# Quotename & geographic quotename, respectively.
  8
+qn = connection.ops.quote_name
  9
+
  10
+class OracleSpatialField(Field):
  11
+    """
  12
+    The backend-specific geographic field for Oracle Spatial.
  13
+    """
  14
+
  15
+    empty_strings_allowed = False
  16
+
  17
+    def __init__(self, extent=(-180.0, -90.0, 180.0, 90.0), tolerance=0.05, **kwargs):
  18
+        """
  19
+        Oracle Spatial backend needs to have the extent -- for projected coordinate
  20
+        systems _you must define the extent manually_, since the coordinates are
  21
+        for geodetic systems.  The `tolerance` keyword specifies the tolerance
  22
+        for error (in meters), and defaults to 0.05 (5 centimeters).
  23
+        """
  24
+        # Oracle Spatial specific keyword arguments.
  25
+        self._extent = extent
  26
+        self._tolerance = tolerance
  27
+        # Calling the Django field initialization.
  28
+        super(OracleSpatialField, self).__init__(**kwargs)
  29
+
  30
+    def _add_geom(self, style, db_table):
  31
+        """
  32
+        Adds this geometry column into the Oracle USER_SDO_GEOM_METADATA
  33
+        table.
  34
+        """
  35
+
  36
+        # Checking the dimensions.
  37
+        # TODO: Add support for 3D geometries.
  38
+        if self._dim != 2:
  39
+            raise Exception('3D geometries not yet supported on Oracle Spatial backend.')
  40
+
  41
+        # Constructing the SQL that will be used to insert information about
  42
+        # the geometry column into the USER_GSDO_GEOM_METADATA table.
  43
+        meta_sql = style.SQL_KEYWORD('INSERT INTO ') + \
  44
+                   style.SQL_TABLE('USER_SDO_GEOM_METADATA') + \
  45
+                   ' (%s, %s, %s, %s)\n  ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) + \
  46
+                   style.SQL_KEYWORD(' VALUES ') + '(\n    ' + \
  47
+                   style.SQL_TABLE(gqn(db_table)) + ',\n    ' + \
  48
+                   style.SQL_FIELD(gqn(self.column)) + ',\n    ' + \
  49
+                   style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n      ' + \
  50
+                   style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
  51
+                   ("('LONG', %s, %s, %s),\n      " % (self._extent[0], self._extent[2], self._tolerance)) + \
  52
+                   style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
  53
+                   ("('LAT', %s, %s, %s)\n    ),\n" % (self._extent[1], self._extent[3], self._tolerance)) + \
  54
+                   '    %s\n  );' % self._srid
  55
+        return meta_sql
  56
+
  57
+    def _geom_index(self, style, db_table):
  58
+        "Creates an Oracle Geometry index (R-tree) for this geometry field."
  59
+
  60
+        # Getting the index name, Oracle doesn't allow object
  61
+        # names > 30 characters.
  62
+        idx_name = truncate_name('%s_%s_id' % (db_table, self.column), 30)
  63
+        
  64
+        sql = style.SQL_KEYWORD('CREATE INDEX ') + \
  65
+              style.SQL_TABLE(qn(idx_name)) + \
  66
+              style.SQL_KEYWORD(' ON ') + \
  67
+              style.SQL_TABLE(qn(db_table)) + '(' + \
  68
+              style.SQL_FIELD(qn(self.column)) + ') ' + \
  69
+              style.SQL_KEYWORD('INDEXTYPE IS ') + \
  70
+              style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';'
  71
+        return sql
  72
+
  73
+    def post_create_sql(self, style, db_table):
  74
+        """
  75
+        Returns SQL that will be executed after the model has been
  76
+        created.
  77
+        """
  78
+        # Getting the meta geometry information.
  79
+        post_sql = self._add_geom(style, db_table)
  80
+
  81
+        # Getting the geometric index for this Geometry column.
  82
+        if self._index:
  83
+            return (post_sql, self._geom_index(style, db_table))
  84
+        else:
  85
+            return (post_sql,)
  86
+
  87
+    def db_type(self):
  88
+        "The Oracle geometric data type is MDSYS.SDO_GEOMETRY."
  89
+        return 'MDSYS.SDO_GEOMETRY'
  90
+        
  91
+    def get_placeholder(self, value):
  92
+        """
  93
+        Provides a proper substitution value for Geometries that are not in the
  94
+        SRID of the field.  Specifically, this routine will substitute in the
  95
+        SDO_CS.TRANSFORM() function call.
  96
+        """
  97
+        if value is None:
  98
+            return '%s'
  99
+        elif value.srid != self._srid:
  100
+            # Adding Transform() to the SQL placeholder.
  101
+            return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self._srid)
  102
+        else:
  103
+            return 'SDO_GEOMETRY(%%s, %s)' % self._srid
49  django/contrib/gis/db/backend/oracle/models.py
... ...
@@ -0,0 +1,49 @@
  1
+"""
  2
+ The GeometryColumns and SpatialRefSys models for the Oracle spatial
  3
+ backend.
  4
+
  5
+ It should be noted that Oracle Spatial does not have database tables
  6
+ named according to the OGC standard, so the closest analogs are used.
  7
+ For example, the `USER_SDO_GEOM_METADATA` is used for the GeometryColumns
  8
+ model and the `SDO_COORD_REF_SYS` is used for the SpatialRefSys model.
  9
+"""
  10
+from django.db import models
  11
+from django.contrib.gis.models import SpatialRefSysMixin
  12
+
  13
+class GeometryColumns(models.Model):
  14
+    "Maps to the Oracle USER_SDO_GEOM_METADATA table."
  15
+    table_name = models.CharField(max_length=32)
  16
+    column_name = models.CharField(max_length=1024)
  17
+    srid = models.IntegerField(primary_key=True)
  18
+    # TODO: Add support for `diminfo` column (type MDSYS.SDO_DIM_ARRAY).
  19
+    class Meta:
  20
+        db_table = 'USER_SDO_GEOM_METADATA'
  21
+
  22
+    @classmethod
  23
+    def table_name_col(cls):
  24
+        return 'table_name'
  25
+
  26
+    def __unicode__(self):
  27
+        return '%s - %s (SRID: %s)' % (self.table_name, self.column_name, self.srid)
  28
+
  29
+class SpatialRefSys(models.Model, SpatialRefSysMixin):
  30
+    "Maps to the Oracle MDSYS.CS_SRS table."
  31
+    cs_name = models.CharField(max_length=68)
  32
+    srid = models.IntegerField(primary_key=True)
  33
+    auth_srid = models.IntegerField()
  34
+    auth_name = models.CharField(max_length=256)
  35
+    wktext = models.CharField(max_length=2046)
  36
+    #cs_bounds = models.GeometryField()
  37
+
  38
+    class Meta:
  39
+        # TODO: Figure out way to have this be MDSYS.CS_SRS without
  40
+        #  having django's quoting mess up the SQL.
  41
+        db_table = 'CS_SRS'
  42
+
  43
+    @property
  44
+    def wkt(self):
  45
+        return self.wktext
  46
+
  47
+    @classmethod
  48
+    def wkt_col(cls):
  49
+        return 'wktext'
154  django/contrib/gis/db/backend/oracle/query.py
... ...
@@ -0,0 +1,154 @@
  1
+"""
  2
+ This module contains the spatial lookup types, and the `get_geo_where_clause`
  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.
  9
+"""
  10
+import re
  11
+from decimal import Decimal
  12
+from django.db import connection
  13
+from django.contrib.gis.db.backend.util import SpatialFunction
  14
+from django.contrib.gis.measure import Distance
  15
+qn = connection.ops.quote_name
  16
+
  17
+# The GML, distance, transform, and union procedures.
  18
+AREA = 'SDO_GEOM.SDO_AREA'
  19
+ASGML = 'SDO_UTIL.TO_GMLGEOMETRY'
  20
+CENTROID = 'SDO_GEOM.SDO_CENTROID'
  21
+DIFFERENCE = 'SDO_GEOM.SDO_DIFFERENCE'
  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'
  30
+TRANSFORM = 'SDO_CS.TRANSFORM'
  31
+UNION = 'SDO_GEOM.SDO_UNION'
  32
+UNIONAGG = 'SDO_AGGR_UNION'
  33
+
  34
+# We want to get SDO Geometries as WKT because it is much easier to 
  35
+# instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.  
  36
+# However, this adversely affects performance (i.e., Java is called 
  37
+# to convert to WKT on every query).  If someone wishes to write a 
  38
+# SDO_GEOMETRY(...) parser in Python, let me know =)
  39
+GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'
  40
+
  41
+#### Classes used in constructing Oracle spatial SQL ####
  42
+class SDOOperation(SpatialFunction):
  43
+    "Base class for SDO* Oracle operations."
  44
+    def __init__(self, func, **kwargs):
  45
+        kwargs.setdefault('operator', '=')
  46
+        kwargs.setdefault('result', 'TRUE')
  47
+        kwargs.setdefault('end_subst', ") %s '%s'")
  48
+        super(SDOOperation, self).__init__(func, **kwargs)
  49
+
  50
+class SDODistance(SpatialFunction):
  51
+    "Class for Distance queries."
  52
+    def __init__(self, op, tolerance=0.05):
  53
+        super(SDODistance, self).__init__(DISTANCE, end_subst=', %s) %%s %%s' % tolerance, 
  54
+                                          operator=op, result='%%s')
  55
+
  56
+class SDOGeomRelate(SpatialFunction):
  57
+    "Class for using SDO_GEOM.RELATE."
  58
+    def __init__(self, mask, tolerance=0.05):
  59
+        # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance.
  60
+        # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE').
  61
+        end_subst = "%s%s) %s '%s'" % (', %%s, ', tolerance, '=', mask)
  62
+        beg_subst = "%%s(%%s, '%s'" % mask 
  63
+        super(SDOGeomRelate, self).__init__('SDO_GEOM.RELATE', beg_subst=beg_subst, end_subst=end_subst)
  64
+
  65
+class SDORelate(SpatialFunction):
  66
+    "Class for using SDO_RELATE."
  67
+    masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON'
  68
+    mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I)
  69
+    def __init__(self, mask):
  70
+        func = 'SDO_RELATE'
  71
+        if not self.mask_regex.match(mask):
  72
+            raise ValueError('Invalid %s mask: "%s"' % (func, mask))
  73
+        super(SDORelate, self).__init__(func, end_subst=", 'mask=%s') = 'TRUE'" % mask)
  74
+
  75
+#### Lookup type mapping dictionaries of Oracle spatial operations ####
  76
+
  77
+# Valid distance types and substitutions
  78
+dtypes = (Decimal, Distance, float, int, long)
  79
+DISTANCE_FUNCTIONS = {
  80
+    'distance_gt' : (SDODistance('>'), dtypes),
  81
+    'distance_gte' : (SDODistance('>='), dtypes),
  82
+    'distance_lt' : (SDODistance('<'), dtypes),
  83
+    'distance_lte' : (SDODistance('<='), dtypes),
  84
+    'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE', 
  85
+                              beg_subst="%s(%s, %%s, 'distance=%%s'"), dtypes),
  86
+    }
  87
+
  88
+ORACLE_GEOMETRY_FUNCTIONS = {
  89
+    'contains' : SDOOperation('SDO_CONTAINS'),
  90
+    'coveredby' : SDOOperation('SDO_COVEREDBY'),
  91
+    'covers' : SDOOperation('SDO_COVERS'),
  92
+    'disjoint' : SDOGeomRelate('DISJOINT'),
  93
+    'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()?
  94
+    'equals' : SDOOperation('SDO_EQUAL'),
  95
+    'exact' : SDOOperation('SDO_EQUAL'),
  96
+    'overlaps' : SDOOperation('SDO_OVERLAPS'),
  97
+    'same_as' : SDOOperation('SDO_EQUAL'),
  98
+    'relate' : (SDORelate, basestring), # Oracle uses a different syntax, e.g., 'mask=inside+touch'
  99
+    'touches' : SDOOperation('SDO_TOUCH'),
  100
+    'within' : SDOOperation('SDO_INSIDE'),
  101
+    }
  102
+ORACLE_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS)
  103
+
  104
+# This lookup type does not require a mapping.
  105
+MISC_TERMS = ['isnull']
  106
+
  107
+# Acceptable lookup types for Oracle spatial.
  108
+ORACLE_SPATIAL_TERMS  = ORACLE_GEOMETRY_FUNCTIONS.keys()
  109
+ORACLE_SPATIAL_TERMS += MISC_TERMS
  110
+ORACLE_SPATIAL_TERMS = dict((term, None) for term in ORACLE_SPATIAL_TERMS) # Making dictionary for fast lookups
  111
+
  112
+#### The `get_geo_where_clause` function for Oracle ####
  113
+def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
  114
+    "Returns the SQL WHERE clause for use in Oracle spatial SQL construction."
  115
+    # Getting the quoted table name as `geo_col`.
  116
+    geo_col = '%s.%s' % (qn(table_alias), qn(name))
  117
+
  118
+    # See if a Oracle Geometry function matches the lookup type next
  119
+    lookup_info = ORACLE_GEOMETRY_FUNCTIONS.get(lookup_type, False)
  120
+    if lookup_info:
  121
+        # Lookup types that are tuples take tuple arguments, e.g., 'relate' and 
  122
+        # 'dwithin' lookup types.
  123
+        if isinstance(lookup_info, tuple):
  124
+            # First element of tuple is lookup type, second element is the type
  125
+            # of the expected argument (e.g., str, float)
  126
+            sdo_op, arg_type = lookup_info
  127
+
  128
+            # Ensuring that a tuple _value_ was passed in from the user
  129
+            if not isinstance(geo_annot.value, tuple):
  130
+                raise TypeError('Tuple required for `%s` lookup type.' % lookup_type)
  131
+            if len(geo_annot.value) != 2: 
  132
+                raise ValueError('2-element tuple required for %s lookup type.' % lookup_type)
  133
+            
  134
+            # Ensuring the argument type matches what we expect.
  135
+            if not isinstance(geo_annot.value[1], arg_type):
  136
+                raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(geo_annot.value[1])))
  137
+
  138
+            if lookup_type == 'relate':
  139
+                # The SDORelate class handles construction for these queries, 
  140
+                # and verifies the mask argument.
  141
+                return sdo_op(geo_annot.value[1]).as_sql(geo_col)
  142
+            else:
  143
+                # Otherwise, just call the `as_sql` method on the SDOOperation instance.
  144
+                return sdo_op.as_sql(geo_col)
  145
+        else:
  146
+            # Lookup info is a SDOOperation instance, whose `as_sql` method returns
  147
+            # the SQL necessary for the geometry function call. For example:  
  148
+            #  SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE'
  149
+            return lookup_info.as_sql(geo_col)
  150
+    elif lookup_type == 'isnull':
  151
+        # Handling 'isnull' lookup type
  152
+        return "%s IS %sNULL" % (geo_col, (not geo_annot.value and 'NOT ' or ''))
  153
+
  154
+    raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
42  django/contrib/gis/db/backend/postgis/__init__.py
... ...
@@ -0,0 +1,42 @@
  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
+                                    )
33  django/contrib/gis/db/backend/postgis/adaptor.py
... ...
@@ -0,0 +1,33 @@
  1
+"""
  2
+ This object provides quoting for GEOS geometries into PostgreSQL/PostGIS.
  3
+"""
  4
+
  5
+from django.contrib.gis.db.backend.postgis.query import GEOM_FROM_WKB
  6
+from psycopg2 import Binary
  7
+from psycopg2.extensions import ISQLQuote
  8
+
  9
+class PostGISAdaptor(object):
  10
+    def __init__(self, geom):
  11
+        "Initializes on the geometry."
  12
+        # Getting the WKB (in string form, to allow easy pickling of
  13
+        # the adaptor) and the SRID from the geometry.
  14
+        self.wkb = str(geom.wkb)
  15
+        self.srid = geom.srid
  16
+
  17
+    def __conform__(self, proto):
  18
+        # Does the given protocol conform to what Psycopg2 expects?
  19
+        if proto == ISQLQuote:
  20
+            return self
  21
+        else:
  22
+            raise Exception('Error implementing psycopg2 protocol. Is psycopg2 installed?')
  23
+
  24
+    def __eq__(self, other):
  25
+        return (self.wkb == other.wkb) and (self.srid == other.srid)
  26
+
  27
+    def __str__(self):
  28
+        return self.getquoted()
  29
+
  30
+    def getquoted(self):
  31
+        "Returns a properly quoted string for use in PostgreSQL/PostGIS."
  32
+        # Want to use WKB, so wrap with psycopg2 Binary() to quote properly.
  33
+        return "%s(%s, %s)" % (GEOM_FROM_WKB, Binary(self.wkb), self.srid or -1)
224  django/contrib/gis/db/backend/postgis/creation.py
... ...
@@ -0,0 +1,224 @@
  1
+from django.conf import settings
  2
+from django.core.management import call_command
  3
+from django.db import connection
  4
+from django.test.utils import _set_autocommit, TEST_DATABASE_PREFIX
  5
+import os, re, sys
  6
+
  7
+def getstatusoutput(cmd):
  8
+    "A simpler version of getstatusoutput that works on win32 platforms."
  9
+    stdin, stdout, stderr = os.popen3(cmd)
  10
+    output = stdout.read()
  11
+    if output.endswith('\n'): output = output[:-1]
  12
+    status = stdin.close()
  13
+    return status, output
  14
+
  15
+def create_lang(db_name, verbosity=1):
  16
+    "Sets up the pl/pgsql language on the given database."
  17
+
  18
+    # Getting the command-line options for the shell command
  19
+    options = get_cmd_options(db_name)
  20
+
  21
+    # Constructing the 'createlang' command.
  22
+    createlang_cmd = 'createlang %splpgsql' % options
  23
+    if verbosity >= 1: print createlang_cmd
  24
+
  25
+    # Must have database super-user privileges to execute createlang -- it must
  26
+    #  also be in your path.
  27
+    status, output = getstatusoutput(createlang_cmd)
  28
+
  29
+    # Checking the status of the command, 0 => execution successful
  30
+    if status:
  31
+        raise Exception("Error executing 'plpgsql' command: %s\n" % output)
  32
+
  33
+def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
  34
+    "Creates database with psycopg2 cursor."
  35
+
  36
+    # Constructing the necessary SQL to create the database (the DATABASE_USER
  37
+    #  must possess the privileges to create a database)
  38
+    create_sql = 'CREATE DATABASE %s' % connection.ops.quote_name(db_name)
  39
+    if settings.DATABASE_USER:
  40
+        create_sql += ' OWNER %s' % settings.DATABASE_USER
  41
+        
  42
+    cursor = connection.cursor()
  43
+    _set_autocommit(connection)
  44
+
  45
+    try:
  46
+        # Trying to create the database first.
  47
+        cursor.execute(create_sql)
  48
+        #print create_sql
  49
+    except Exception, e:
  50
+        # Drop and recreate, if necessary.
  51
+        if not autoclobber:
  52
+            confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
  53
+        if autoclobber or confirm == 'yes':
  54
+            if verbosity >= 1: print 'Destroying old spatial database...'
  55
+            drop_db(db_name)
  56
+            if verbosity >= 1: print 'Creating new spatial database...'
  57
+            cursor.execute(create_sql)
  58
+        else:
  59
+            raise Exception('Spatial Database Creation canceled.')
  60
+foo = _create_with_cursor
  61
+    
  62
+created_regex = re.compile(r'^createdb: database creation failed: ERROR:  database ".+" already exists')
  63
+def _create_with_shell(db_name, verbosity=1, autoclobber=False):
  64
+    """
  65
+    If no spatial database already exists, then using a cursor will not work.  
  66
+     Thus, a `createdb` command will be issued through the shell to bootstrap 
  67
+     creation of the spatial database.
  68
+    """
  69
+
  70
+    # Getting the command-line options for the shell command
  71
+    options = get_cmd_options(False)
  72
+    create_cmd = 'createdb -O %s %s%s' % (settings.DATABASE_USER, options, db_name)
  73
+    if verbosity >= 1: print create_cmd
  74
+
  75
+    # Attempting to create the database.
  76
+    status, output = getstatusoutput(create_cmd)
  77
+
  78
+    if status:
  79
+        if created_regex.match(output):
  80
+            if not autoclobber:
  81
+                confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
  82
+            if autoclobber or confirm == 'yes':
  83
+                if verbosity >= 1: print 'Destroying old spatial database...'
  84
+                drop_cmd = 'dropdb %s%s' % (options, db_name)
  85
+                status, output = getstatusoutput(drop_cmd)
  86
+                if status != 0: 
  87
+                    raise Exception('Could not drop database %s: %s' % (db_name, output))
  88
+                if verbosity >= 1: print 'Creating new spatial database...'
  89
+                status, output = getstatusoutput(create_cmd)
  90
+                if status != 0:
  91
+                    raise Exception('Could not create database after dropping: %s' % output)
  92
+            else:
  93
+                raise Exception('Spatial Database Creation canceled.')
  94
+        else:
  95
+            raise Exception('Unknown error occurred in creating database: %s' % output)
  96
+
  97
+def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=False):
  98
+    "Creates a spatial database based on the settings."
  99
+
  100
+    # Making sure we're using PostgreSQL and psycopg2
  101
+    if settings.DATABASE_ENGINE != 'postgresql_psycopg2':
  102
+        raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.')
  103
+
  104
+    # Getting the spatial database name
  105
+    if test: 
  106
+        db_name = get_spatial_db(test=True)
  107
+        _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
  108
+    else: 
  109
+        db_name = get_spatial_db()
  110
+        _create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber)
  111
+
  112
+    # Creating the db language, does not need to be done on NT platforms
  113
+    #  since the PostGIS installer enables this capability.
  114
+    if os.name != 'nt':
  115
+        create_lang(db_name, verbosity=verbosity)
  116
+
  117
+    # Now adding in the PostGIS routines.
  118
+    load_postgis_sql(db_name, verbosity=verbosity)
  119
+
  120
+    if verbosity >= 1: print 'Creation of spatial database %s successful.' % db_name
  121
+
  122
+    # Closing the connection
  123
+    connection.close()
  124
+    settings.DATABASE_NAME = db_name
  125
+
  126
+    # Syncing the database
  127
+    call_command('syncdb', verbosity=verbosity, interactive=interactive)
  128
+    
  129
+def drop_db(db_name=False, test=False):
  130
+    """
  131
+    Drops the given database (defaults to what is returned from
  132
+     get_spatial_db()). All exceptions are propagated up to the caller.
  133
+    """
  134
+    if not db_name: db_name = get_spatial_db(test=test)
&n