summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/django/contrib/gis/db/backends
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/django/contrib/gis/db/backends')
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/__init__.py0
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/adapter.py19
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/base.py349
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/__init__.py0
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/base.py13
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/compiler.py35
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/creation.py18
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/introspection.py32
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/operations.py67
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/__init__.py0
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/adapter.py5
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/base.py12
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/compiler.py25
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/creation.py42
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/introspection.py44
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/models.py66
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/operations.py304
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/__init__.py0
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/adapter.py46
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/base.py12
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/creation.py95
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/introspection.py103
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/models.py68
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/operations.py569
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/__init__.py0
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py8
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/base.py60
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/client.py5
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/creation.py129
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py52
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/models.py62
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/operations.py373
-rw-r--r--lib/python2.7/site-packages/django/contrib/gis/db/backends/util.py44
33 files changed, 2657 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/__init__.py
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/adapter.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/adapter.py
new file mode 100644
index 0000000..ca77124
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/adapter.py
@@ -0,0 +1,19 @@
+class WKTAdapter(object):
+ """
+ This provides an adaptor for Geometries sent to the
+ MySQL and Oracle database backends.
+ """
+ def __init__(self, geom):
+ self.wkt = geom.wkt
+ self.srid = geom.srid
+
+ def __eq__(self, other):
+ if not isinstance(other, WKTAdapter):
+ return False
+ return self.wkt == other.wkt and self.srid == other.srid
+
+ def __str__(self):
+ return self.wkt
+
+ def prepare_database_save(self, unused):
+ return self
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/base.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/base.py
new file mode 100644
index 0000000..7db7ce5
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/base.py
@@ -0,0 +1,349 @@
+"""
+Base/mixin classes for the spatial backend database operations and the
+`SpatialRefSys` model the backend.
+"""
+import re
+
+from django.contrib.gis import gdal
+from django.utils import six
+from django.utils.encoding import python_2_unicode_compatible
+
+
+class BaseSpatialOperations(object):
+ """
+ This module holds the base `BaseSpatialBackend` object, which is
+ instantiated by each spatial database backend with the features
+ it has.
+ """
+ distance_functions = {}
+ geometry_functions = {}
+ geometry_operators = {}
+ geography_operators = {}
+ geography_functions = {}
+ gis_terms = set()
+ truncate_params = {}
+
+ # Quick booleans for the type of this spatial backend, and
+ # an attribute for the spatial database version tuple (if applicable)
+ postgis = False
+ spatialite = False
+ mysql = False
+ oracle = False
+ spatial_version = None
+
+ # How the geometry column should be selected.
+ select = None
+
+ # Does the spatial database have a geometry or geography type?
+ geography = False
+ geometry = False
+
+ area = False
+ centroid = False
+ difference = False
+ distance = False
+ distance_sphere = False
+ distance_spheroid = False
+ envelope = False
+ force_rhr = False
+ mem_size = False
+ bounding_circle = False
+ num_geom = False
+ num_points = False
+ perimeter = False
+ perimeter3d = False
+ point_on_surface = False
+ polygonize = False
+ reverse = False
+ scale = False
+ snap_to_grid = False
+ sym_difference = False
+ transform = False
+ translate = False
+ union = False
+
+ # Aggregates
+ collect = False
+ extent = False
+ extent3d = False
+ make_line = False
+ unionagg = False
+
+ # Serialization
+ geohash = False
+ geojson = False
+ gml = False
+ kml = False
+ svg = False
+
+ # Constructors
+ from_text = False
+ from_wkb = False
+
+ # Default conversion functions for aggregates; will be overridden if implemented
+ # for the spatial backend.
+ def convert_extent(self, box):
+ raise NotImplementedError('Aggregate extent not implemented for this spatial backend.')
+
+ def convert_extent3d(self, box):
+ raise NotImplementedError('Aggregate 3D extent not implemented for this spatial backend.')
+
+ def convert_geom(self, geom_val, geom_field):
+ raise NotImplementedError('Aggregate method not implemented for this spatial backend.')
+
+ # For quoting column values, rather than columns.
+ def geo_quote_name(self, name):
+ return "'%s'" % name
+
+ # GeometryField operations
+ def geo_db_type(self, f):
+ """
+ Returns the database column type for the geometry field on
+ the spatial backend.
+ """
+ raise NotImplementedError
+
+ def get_distance(self, f, value, lookup_type):
+ """
+ Returns the distance parameters for the given geometry field,
+ lookup value, and lookup type.
+ """
+ raise NotImplementedError('Distance operations not available on this spatial backend.')
+
+ def get_geom_placeholder(self, f, value):
+ """
+ Returns the placeholder for the given geometry field with the given
+ value. Depending on the spatial backend, the placeholder may contain a
+ stored procedure call to the transformation function of the spatial
+ backend.
+ """
+ raise NotImplementedError
+
+ def get_expression_column(self, evaluator):
+ """
+ Helper method to return the quoted column string from the evaluator
+ for its expression.
+ """
+ for expr, col_tup in evaluator.cols:
+ if expr is evaluator.expression:
+ return '%s.%s' % tuple(map(self.quote_name, col_tup))
+ raise Exception("Could not find the column for the expression.")
+
+ # Spatial SQL Construction
+ def spatial_aggregate_sql(self, agg):
+ raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
+
+ def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
+ raise NotImplementedError
+
+ # Routines for getting the OGC-compliant models.
+ def geometry_columns(self):
+ raise NotImplementedError
+
+ def spatial_ref_sys(self):
+ raise NotImplementedError
+
+@python_2_unicode_compatible
+class SpatialRefSysMixin(object):
+ """
+ The SpatialRefSysMixin is a class used by the database-dependent
+ SpatialRefSys objects to reduce redundnant code.
+ """
+ # For pulling out the spheroid from the spatial reference string. This
+ # regular expression is used only if the user does not have GDAL installed.
+ # TODO: Flattening not used in all ellipsoids, could also be a minor axis,
+ # or 'b' parameter.
+ spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
+
+ # For pulling out the units on platforms w/o GDAL installed.
+ # TODO: Figure out how to pull out angular units of projected coordinate system and
+ # fix for LOCAL_CS types. GDAL should be highly recommended for performing
+ # distance queries.
+ units_regex = re.compile(r'.+UNIT ?\["(?P<unit_name>[\w \'\(\)]+)", ?(?P<unit>[\d\.]+)(,AUTHORITY\["(?P<unit_auth_name>[\w \'\(\)]+)","(?P<unit_auth_val>\d+)"\])?\]([\w ]+)?(,AUTHORITY\["(?P<auth_name>[\w \'\(\)]+)","(?P<auth_val>\d+)"\])?\]$')
+
+ @property
+ def srs(self):
+ """
+ Returns a GDAL SpatialReference object, if GDAL is installed.
+ """
+ if gdal.HAS_GDAL:
+ # TODO: Is caching really necessary here? Is complexity worth it?
+ if hasattr(self, '_srs'):
+ # Returning a clone of the cached SpatialReference object.
+ return self._srs.clone()
+ else:
+ # Attempting to cache a SpatialReference object.
+
+ # Trying to get from WKT first.
+ try:
+ self._srs = gdal.SpatialReference(self.wkt)
+ return self.srs
+ except Exception as msg:
+ pass
+
+ try:
+ self._srs = gdal.SpatialReference(self.proj4text)
+ return self.srs
+ except Exception as msg:
+ pass
+
+ raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
+ else:
+ raise Exception('GDAL is not installed.')
+
+ @property
+ def ellipsoid(self):
+ """
+ Returns a tuple of the ellipsoid parameters:
+ (semimajor axis, semiminor axis, and inverse flattening).
+ """
+ if gdal.HAS_GDAL:
+ return self.srs.ellipsoid
+ else:
+ m = self.spheroid_regex.match(self.wkt)
+ if m: return (float(m.group('major')), float(m.group('flattening')))
+ else: return None
+
+ @property
+ def name(self):
+ "Returns the projection name."
+ return self.srs.name
+
+ @property
+ def spheroid(self):
+ "Returns the spheroid name for this spatial reference."
+ return self.srs['spheroid']
+
+ @property
+ def datum(self):
+ "Returns the datum for this spatial reference."
+ return self.srs['datum']
+
+ @property
+ def projected(self):
+ "Is this Spatial Reference projected?"
+ if gdal.HAS_GDAL:
+ return self.srs.projected
+ else:
+ return self.wkt.startswith('PROJCS')
+
+ @property
+ def local(self):
+ "Is this Spatial Reference local?"
+ if gdal.HAS_GDAL:
+ return self.srs.local
+ else:
+ return self.wkt.startswith('LOCAL_CS')
+
+ @property
+ def geographic(self):
+ "Is this Spatial Reference geographic?"
+ if gdal.HAS_GDAL:
+ return self.srs.geographic
+ else:
+ return self.wkt.startswith('GEOGCS')
+
+ @property
+ def linear_name(self):
+ "Returns the linear units name."
+ if gdal.HAS_GDAL:
+ return self.srs.linear_name
+ elif self.geographic:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit_name')
+
+ @property
+ def linear_units(self):
+ "Returns the linear units."
+ if gdal.HAS_GDAL:
+ return self.srs.linear_units
+ elif self.geographic:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit')
+
+ @property
+ def angular_name(self):
+ "Returns the name of the angular units."
+ if gdal.HAS_GDAL:
+ return self.srs.angular_name
+ elif self.projected:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit_name')
+
+ @property
+ def angular_units(self):
+ "Returns the angular units."
+ if gdal.HAS_GDAL:
+ return self.srs.angular_units
+ elif self.projected:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit')
+
+ @property
+ def units(self):
+ "Returns a tuple of the units and the name."
+ if self.projected or self.local:
+ return (self.linear_units, self.linear_name)
+ elif self.geographic:
+ return (self.angular_units, self.angular_name)
+ else:
+ return (None, None)
+
+ @classmethod
+ def get_units(cls, wkt):
+ """
+ Class method used by GeometryField on initialization to
+ retrive the units on the given WKT, without having to use
+ any of the database fields.
+ """
+ if gdal.HAS_GDAL:
+ return gdal.SpatialReference(wkt).units
+ else:
+ m = cls.units_regex.match(wkt)
+ return m.group('unit'), m.group('unit_name')
+
+ @classmethod
+ def get_spheroid(cls, wkt, string=True):
+ """
+ Class method used by GeometryField on initialization to
+ retrieve the `SPHEROID[..]` parameters from the given WKT.
+ """
+ if gdal.HAS_GDAL:
+ srs = gdal.SpatialReference(wkt)
+ sphere_params = srs.ellipsoid
+ sphere_name = srs['spheroid']
+ else:
+ m = cls.spheroid_regex.match(wkt)
+ if m:
+ sphere_params = (float(m.group('major')), float(m.group('flattening')))
+ sphere_name = m.group('name')
+ else:
+ return None
+
+ if not string:
+ return sphere_name, sphere_params
+ else:
+ # `string` parameter used to place in format acceptable by PostGIS
+ if len(sphere_params) == 3:
+ radius, flattening = sphere_params[0], sphere_params[2]
+ else:
+ radius, flattening = sphere_params
+ return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening)
+
+ def __str__(self):
+ """
+ Returns the string representation. If GDAL is installed,
+ it will be 'pretty' OGC WKT.
+ """
+ try:
+ return six.text_type(self.srs)
+ except:
+ return six.text_type(self.wkt)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/__init__.py
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/base.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/base.py
new file mode 100644
index 0000000..7d94458
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/base.py
@@ -0,0 +1,13 @@
+from django.db.backends.mysql.base import *
+from django.db.backends.mysql.base import DatabaseWrapper as MySQLDatabaseWrapper
+from django.contrib.gis.db.backends.mysql.creation import MySQLCreation
+from django.contrib.gis.db.backends.mysql.introspection import MySQLIntrospection
+from django.contrib.gis.db.backends.mysql.operations import MySQLOperations
+
+class DatabaseWrapper(MySQLDatabaseWrapper):
+
+ def __init__(self, *args, **kwargs):
+ super(DatabaseWrapper, self).__init__(*args, **kwargs)
+ self.creation = MySQLCreation(self)
+ self.ops = MySQLOperations(self)
+ self.introspection = MySQLIntrospection(self)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/compiler.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/compiler.py
new file mode 100644
index 0000000..f4654ef
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/compiler.py
@@ -0,0 +1,35 @@
+from django.contrib.gis.db.models.sql.compiler import GeoSQLCompiler as BaseGeoSQLCompiler
+from django.db.backends.mysql import compiler
+
+SQLCompiler = compiler.SQLCompiler
+
+class GeoSQLCompiler(BaseGeoSQLCompiler, SQLCompiler):
+ def resolve_columns(self, row, fields=()):
+ """
+ Integrate the cases handled both by the base GeoSQLCompiler and the
+ main MySQL compiler (converting 0/1 to True/False for boolean fields).
+
+ Refs #15169.
+
+ """
+ row = BaseGeoSQLCompiler.resolve_columns(self, row, fields)
+ return SQLCompiler.resolve_columns(self, row, fields)
+
+
+class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler):
+ pass
+
+class SQLUpdateCompiler(compiler.SQLUpdateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDateTimeCompiler(compiler.SQLDateTimeCompiler, GeoSQLCompiler):
+ pass
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/creation.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/creation.py
new file mode 100644
index 0000000..dda77ea
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/creation.py
@@ -0,0 +1,18 @@
+from django.db.backends.mysql.creation import DatabaseCreation
+
+class MySQLCreation(DatabaseCreation):
+
+ def sql_indexes_for_field(self, model, f, style):
+ from django.contrib.gis.db.models.fields import GeometryField
+ output = super(MySQLCreation, self).sql_indexes_for_field(model, f, style)
+
+ if isinstance(f, GeometryField) and f.spatial_index:
+ qn = self.connection.ops.quote_name
+ db_table = model._meta.db_table
+ idx_name = '%s_%s_id' % (db_table, f.column)
+ output.append(style.SQL_KEYWORD('CREATE SPATIAL INDEX ') +
+ style.SQL_TABLE(qn(idx_name)) +
+ style.SQL_KEYWORD(' ON ') +
+ style.SQL_TABLE(qn(db_table)) + '(' +
+ style.SQL_FIELD(qn(f.column)) + ');')
+ return output
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/introspection.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/introspection.py
new file mode 100644
index 0000000..59d0f62
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/introspection.py
@@ -0,0 +1,32 @@
+from MySQLdb.constants import FIELD_TYPE
+
+from django.contrib.gis.gdal import OGRGeomType
+from django.db.backends.mysql.introspection import DatabaseIntrospection
+
+class MySQLIntrospection(DatabaseIntrospection):
+ # Updating the data_types_reverse dictionary with the appropriate
+ # type for Geometry fields.
+ data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
+ data_types_reverse[FIELD_TYPE.GEOMETRY] = 'GeometryField'
+
+ def get_geometry_type(self, table_name, geo_col):
+ cursor = self.connection.cursor()
+ try:
+ # In order to get the specific geometry type of the field,
+ # we introspect on the table definition using `DESCRIBE`.
+ cursor.execute('DESCRIBE %s' %
+ self.connection.ops.quote_name(table_name))
+ # Increment over description info until we get to the geometry
+ # column.
+ for column, typ, null, key, default, extra in cursor.fetchall():
+ if column == geo_col:
+ # Using OGRGeomType to convert from OGC name to Django field.
+ # MySQL does not support 3D or SRIDs, so the field params
+ # are empty.
+ field_type = OGRGeomType(typ).django
+ field_params = {}
+ break
+ finally:
+ cursor.close()
+
+ return field_type, field_params
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/operations.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/operations.py
new file mode 100644
index 0000000..26cec74
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/mysql/operations.py
@@ -0,0 +1,67 @@
+from django.db.backends.mysql.base import DatabaseOperations
+
+from django.contrib.gis.db.backends.adapter import WKTAdapter
+from django.contrib.gis.db.backends.base import BaseSpatialOperations
+
+
+class MySQLOperations(DatabaseOperations, BaseSpatialOperations):
+
+ compiler_module = 'django.contrib.gis.db.backends.mysql.compiler'
+ mysql = True
+ name = 'mysql'
+ select = 'AsText(%s)'
+ from_wkb = 'GeomFromWKB'
+ from_text = 'GeomFromText'
+
+ Adapter = WKTAdapter
+ Adaptor = Adapter # Backwards-compatibility alias.
+
+ geometry_functions = {
+ 'bbcontains': 'MBRContains', # For consistency w/PostGIS API
+ 'bboverlaps': 'MBROverlaps', # .. ..
+ 'contained': 'MBRWithin', # .. ..
+ 'contains': 'MBRContains',
+ 'disjoint': 'MBRDisjoint',
+ 'equals': 'MBREqual',
+ 'exact': 'MBREqual',
+ 'intersects': 'MBRIntersects',
+ 'overlaps': 'MBROverlaps',
+ 'same_as': 'MBREqual',
+ 'touches': 'MBRTouches',
+ 'within': 'MBRWithin',
+ }
+
+ gis_terms = set(geometry_functions) | set(['isnull'])
+
+ def geo_db_type(self, f):
+ return f.geom_type
+
+ def get_geom_placeholder(self, value, srid):
+ """
+ The placeholder here has to include MySQL's WKT constructor. Because
+ MySQL does not support spatial transformations, there is no need to
+ modify the placeholder based on the contents of the given value.
+ """
+ if hasattr(value, 'expression'):
+ placeholder = self.get_expression_column(value)
+ else:
+ placeholder = '%s(%%s)' % self.from_text
+ return placeholder
+
+ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn):
+ alias, col, db_type = lvalue
+
+ geo_col = '%s.%s' % (qn(alias), qn(col))
+
+ lookup_info = self.geometry_functions.get(lookup_type, False)
+ if lookup_info:
+ sql = "%s(%s, %s)" % (lookup_info, geo_col,
+ self.get_geom_placeholder(value, field.srid))
+ return sql, []
+
+ # TODO: Is this really necessary? MySQL can't handle NULL geometries
+ # in its spatial indexes anyways.
+ if lookup_type == 'isnull':
+ return "%s IS %sNULL" % (geo_col, ('' if value else 'NOT ')), []
+
+ raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/__init__.py
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/adapter.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/adapter.py
new file mode 100644
index 0000000..ea340d9
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/adapter.py
@@ -0,0 +1,5 @@
+from cx_Oracle import CLOB
+from django.contrib.gis.db.backends.adapter import WKTAdapter
+
+class OracleSpatialAdapter(WKTAdapter):
+ input_size = CLOB
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/base.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/base.py
new file mode 100644
index 0000000..398b3d3
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/base.py
@@ -0,0 +1,12 @@
+from django.db.backends.oracle.base import *
+from django.db.backends.oracle.base import DatabaseWrapper as OracleDatabaseWrapper
+from django.contrib.gis.db.backends.oracle.creation import OracleCreation
+from django.contrib.gis.db.backends.oracle.introspection import OracleIntrospection
+from django.contrib.gis.db.backends.oracle.operations import OracleOperations
+
+class DatabaseWrapper(OracleDatabaseWrapper):
+ def __init__(self, *args, **kwargs):
+ super(DatabaseWrapper, self).__init__(*args, **kwargs)
+ self.ops = OracleOperations(self)
+ self.creation = OracleCreation(self)
+ self.introspection = OracleIntrospection(self)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/compiler.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/compiler.py
new file mode 100644
index 0000000..d00af7f
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/compiler.py
@@ -0,0 +1,25 @@
+from django.contrib.gis.db.models.sql.compiler import GeoSQLCompiler as BaseGeoSQLCompiler
+from django.db.backends.oracle import compiler
+
+SQLCompiler = compiler.SQLCompiler
+
+class GeoSQLCompiler(BaseGeoSQLCompiler, SQLCompiler):
+ pass
+
+class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler):
+ pass
+
+class SQLUpdateCompiler(compiler.SQLUpdateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler):
+ pass
+
+class SQLDateTimeCompiler(compiler.SQLDateTimeCompiler, GeoSQLCompiler):
+ pass
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/creation.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/creation.py
new file mode 100644
index 0000000..043da91
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/creation.py
@@ -0,0 +1,42 @@
+from django.db.backends.oracle.creation import DatabaseCreation
+from django.db.backends.util import truncate_name
+
+class OracleCreation(DatabaseCreation):
+
+ def sql_indexes_for_field(self, model, f, style):
+ "Return any spatial index creation SQL for the field."
+ from django.contrib.gis.db.models.fields import GeometryField
+
+ output = super(OracleCreation, self).sql_indexes_for_field(model, f, style)
+
+ if isinstance(f, GeometryField):
+ gqn = self.connection.ops.geo_quote_name
+ qn = self.connection.ops.quote_name
+ db_table = model._meta.db_table
+
+ output.append(style.SQL_KEYWORD('INSERT INTO ') +
+ style.SQL_TABLE('USER_SDO_GEOM_METADATA') +
+ ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) +
+ style.SQL_KEYWORD(' VALUES ') + '(\n ' +
+ style.SQL_TABLE(gqn(db_table)) + ',\n ' +
+ style.SQL_FIELD(gqn(f.column)) + ',\n ' +
+ style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' +
+ style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
+ ("('LONG', %s, %s, %s),\n " % (f._extent[0], f._extent[2], f._tolerance)) +
+ style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
+ ("('LAT', %s, %s, %s)\n ),\n" % (f._extent[1], f._extent[3], f._tolerance)) +
+ ' %s\n );' % f.srid)
+
+ if f.spatial_index:
+ # Getting the index name, Oracle doesn't allow object
+ # names > 30 characters.
+ idx_name = truncate_name('%s_%s_id' % (db_table, f.column), 30)
+
+ output.append(style.SQL_KEYWORD('CREATE INDEX ') +
+ style.SQL_TABLE(qn(idx_name)) +
+ style.SQL_KEYWORD(' ON ') +
+ style.SQL_TABLE(qn(db_table)) + '(' +
+ style.SQL_FIELD(qn(f.column)) + ') ' +
+ style.SQL_KEYWORD('INDEXTYPE IS ') +
+ style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';')
+ return output
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/introspection.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/introspection.py
new file mode 100644
index 0000000..d6c8f45
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/introspection.py
@@ -0,0 +1,44 @@
+import cx_Oracle
+import sys
+from django.db.backends.oracle.introspection import DatabaseIntrospection
+from django.utils import six
+
+class OracleIntrospection(DatabaseIntrospection):
+ # Associating any OBJECTVAR instances with GeometryField. Of course,
+ # this won't work right on Oracle objects that aren't MDSYS.SDO_GEOMETRY,
+ # but it is the only object type supported within Django anyways.
+ data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
+ data_types_reverse[cx_Oracle.OBJECT] = 'GeometryField'
+
+ def get_geometry_type(self, table_name, geo_col):
+ cursor = self.connection.cursor()
+ try:
+ # Querying USER_SDO_GEOM_METADATA to get the SRID and dimension information.
+ try:
+ cursor.execute('SELECT "DIMINFO", "SRID" FROM "USER_SDO_GEOM_METADATA" WHERE "TABLE_NAME"=%s AND "COLUMN_NAME"=%s',
+ (table_name.upper(), geo_col.upper()))
+ row = cursor.fetchone()
+ except Exception as msg:
+ new_msg = (
+ 'Could not find entry in USER_SDO_GEOM_METADATA '
+ 'corresponding to "%s"."%s"\n'
+ 'Error message: %s.') % (table_name, geo_col, msg)
+ six.reraise(Exception, Exception(new_msg), sys.exc_info()[2])
+
+ # TODO: Research way to find a more specific geometry field type for
+ # the column's contents.
+ field_type = 'GeometryField'
+
+ # Getting the field parameters.
+ field_params = {}
+ dim, srid = row
+ if srid != 4326:
+ field_params['srid'] = srid
+ # Length of object array ( SDO_DIM_ARRAY ) is number of dimensions.
+ dim = len(dim)
+ if dim != 2:
+ field_params['dim'] = dim
+ finally:
+ cursor.close()
+
+ return field_type, field_params
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/models.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/models.py
new file mode 100644
index 0000000..b7deb3a
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/models.py
@@ -0,0 +1,66 @@
+"""
+ The GeometryColumns and SpatialRefSys models for the Oracle spatial
+ backend.
+
+ It should be noted that Oracle Spatial does not have database tables
+ named according to the OGC standard, so the closest analogs are used.
+ For example, the `USER_SDO_GEOM_METADATA` is used for the GeometryColumns
+ model and the `SDO_COORD_REF_SYS` is used for the SpatialRefSys model.
+"""
+from django.contrib.gis.db import models
+from django.contrib.gis.db.backends.base import SpatialRefSysMixin
+from django.utils.encoding import python_2_unicode_compatible
+
+@python_2_unicode_compatible
+class GeometryColumns(models.Model):
+ "Maps to the Oracle USER_SDO_GEOM_METADATA table."
+ table_name = models.CharField(max_length=32)
+ column_name = models.CharField(max_length=1024)
+ srid = models.IntegerField(primary_key=True)
+ # TODO: Add support for `diminfo` column (type MDSYS.SDO_DIM_ARRAY).
+ class Meta:
+ db_table = 'USER_SDO_GEOM_METADATA'
+ managed = False
+
+ @classmethod
+ def table_name_col(cls):
+ """
+ Returns the name of the metadata column used to store the
+ the feature table name.
+ """
+ return 'table_name'
+
+ @classmethod
+ def geom_col_name(cls):
+ """
+ Returns the name of the metadata column used to store the
+ the feature geometry column.
+ """
+ return 'column_name'
+
+ def __str__(self):
+ return '%s - %s (SRID: %s)' % (self.table_name, self.column_name, self.srid)
+
+class SpatialRefSys(models.Model, SpatialRefSysMixin):
+ "Maps to the Oracle MDSYS.CS_SRS table."
+ cs_name = models.CharField(max_length=68)
+ srid = models.IntegerField(primary_key=True)
+ auth_srid = models.IntegerField()
+ auth_name = models.CharField(max_length=256)
+ wktext = models.CharField(max_length=2046)
+ # Optional geometry representing the bounds of this coordinate
+ # system. By default, all are NULL in the table.
+ cs_bounds = models.PolygonField(null=True)
+ objects = models.GeoManager()
+
+ class Meta:
+ db_table = 'CS_SRS'
+ managed = False
+
+ @property
+ def wkt(self):
+ return self.wktext
+
+ @classmethod
+ def wkt_col(cls):
+ return 'wktext'
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/operations.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/operations.py
new file mode 100644
index 0000000..84217c3
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/oracle/operations.py
@@ -0,0 +1,304 @@
+"""
+ This module contains the spatial lookup types, and the `get_geo_where_clause`
+ routine for Oracle Spatial.
+
+ Please note that WKT support is broken on the XE version, and thus
+ this backend will not work on such platforms. Specifically, XE lacks
+ support for an internal JVM, and Java libraries are required to use
+ the WKT constructors.
+"""
+import re
+from decimal import Decimal
+
+from django.db.backends.oracle.base import DatabaseOperations
+from django.contrib.gis.db.backends.base import BaseSpatialOperations
+from django.contrib.gis.db.backends.oracle.adapter import OracleSpatialAdapter
+from django.contrib.gis.db.backends.util import SpatialFunction
+from django.contrib.gis.geometry.backend import Geometry
+from django.contrib.gis.measure import Distance
+from django.utils import six
+
+
+class SDOOperation(SpatialFunction):
+ "Base class for SDO* Oracle operations."
+ sql_template = "%(function)s(%(geo_col)s, %(geometry)s) %(operator)s '%(result)s'"
+
+ def __init__(self, func, **kwargs):
+ kwargs.setdefault('operator', '=')
+ kwargs.setdefault('result', 'TRUE')
+ super(SDOOperation, self).__init__(func, **kwargs)
+
+class SDODistance(SpatialFunction):
+ "Class for Distance queries."
+ sql_template = ('%(function)s(%(geo_col)s, %(geometry)s, %(tolerance)s) '
+ '%(operator)s %(result)s')
+ dist_func = 'SDO_GEOM.SDO_DISTANCE'
+
+ def __init__(self, op, tolerance=0.05):
+ super(SDODistance, self).__init__(self.dist_func,
+ tolerance=tolerance,
+ operator=op, result='%s')
+
+class SDODWithin(SpatialFunction):
+ dwithin_func = 'SDO_WITHIN_DISTANCE'
+ sql_template = "%(function)s(%(geo_col)s, %(geometry)s, %%s) = 'TRUE'"
+
+ def __init__(self):
+ super(SDODWithin, self).__init__(self.dwithin_func)
+
+class SDOGeomRelate(SpatialFunction):
+ "Class for using SDO_GEOM.RELATE."
+ relate_func = 'SDO_GEOM.RELATE'
+ sql_template = ("%(function)s(%(geo_col)s, '%(mask)s', %(geometry)s, "
+ "%(tolerance)s) %(operator)s '%(mask)s'")
+
+ def __init__(self, mask, tolerance=0.05):
+ # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance.
+ # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE').
+ super(SDOGeomRelate, self).__init__(self.relate_func, operator='=',
+ mask=mask, tolerance=tolerance)
+
+class SDORelate(SpatialFunction):
+ "Class for using SDO_RELATE."
+ masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON'
+ mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I)
+ sql_template = "%(function)s(%(geo_col)s, %(geometry)s, 'mask=%(mask)s') = 'TRUE'"
+ relate_func = 'SDO_RELATE'
+
+ def __init__(self, mask):
+ if not self.mask_regex.match(mask):
+ raise ValueError('Invalid %s mask: "%s"' % (self.relate_func, mask))
+ super(SDORelate, self).__init__(self.relate_func, mask=mask)
+
+# Valid distance types and substitutions
+dtypes = (Decimal, Distance, float) + six.integer_types
+
+class OracleOperations(DatabaseOperations, BaseSpatialOperations):
+ compiler_module = "django.contrib.gis.db.backends.oracle.compiler"
+
+ name = 'oracle'
+ oracle = True
+ valid_aggregates = dict([(a, None) for a in ('Union', 'Extent')])
+
+ Adapter = OracleSpatialAdapter
+ Adaptor = Adapter # Backwards-compatibility alias.
+
+ area = 'SDO_GEOM.SDO_AREA'
+ gml = 'SDO_UTIL.TO_GMLGEOMETRY'
+ centroid = 'SDO_GEOM.SDO_CENTROID'
+ difference = 'SDO_GEOM.SDO_DIFFERENCE'
+ distance = 'SDO_GEOM.SDO_DISTANCE'
+ extent = 'SDO_AGGR_MBR'
+ intersection = 'SDO_GEOM.SDO_INTERSECTION'
+ length = 'SDO_GEOM.SDO_LENGTH'
+ num_geom = 'SDO_UTIL.GETNUMELEM'
+ num_points = 'SDO_UTIL.GETNUMVERTICES'
+ perimeter = length
+ point_on_surface = 'SDO_GEOM.SDO_POINTONSURFACE'
+ reverse = 'SDO_UTIL.REVERSE_LINESTRING'
+ sym_difference = 'SDO_GEOM.SDO_XOR'
+ transform = 'SDO_CS.TRANSFORM'
+ union = 'SDO_GEOM.SDO_UNION'
+ unionagg = 'SDO_AGGR_UNION'
+
+ # We want to get SDO Geometries as WKT because it is much easier to
+ # instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.
+ # However, this adversely affects performance (i.e., Java is called
+ # to convert to WKT on every query). If someone wishes to write a
+ # SDO_GEOMETRY(...) parser in Python, let me know =)
+ select = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'
+
+ distance_functions = {
+ 'distance_gt' : (SDODistance('>'), dtypes),
+ 'distance_gte' : (SDODistance('>='), dtypes),
+ 'distance_lt' : (SDODistance('<'), dtypes),
+ 'distance_lte' : (SDODistance('<='), dtypes),
+ 'dwithin' : (SDODWithin(), dtypes),
+ }
+
+ geometry_functions = {
+ 'contains' : SDOOperation('SDO_CONTAINS'),
+ 'coveredby' : SDOOperation('SDO_COVEREDBY'),
+ 'covers' : SDOOperation('SDO_COVERS'),
+ 'disjoint' : SDOGeomRelate('DISJOINT'),
+ 'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()?
+ 'equals' : SDOOperation('SDO_EQUAL'),
+ 'exact' : SDOOperation('SDO_EQUAL'),
+ 'overlaps' : SDOOperation('SDO_OVERLAPS'),
+ 'same_as' : SDOOperation('SDO_EQUAL'),
+ 'relate' : (SDORelate, six.string_types), # Oracle uses a different syntax, e.g., 'mask=inside+touch'
+ 'touches' : SDOOperation('SDO_TOUCH'),
+ 'within' : SDOOperation('SDO_INSIDE'),
+ }
+ geometry_functions.update(distance_functions)
+
+ gis_terms = set(['isnull'])
+ gis_terms.update(geometry_functions)
+
+ truncate_params = {'relate' : None}
+
+ def convert_extent(self, clob):
+ if clob:
+ # Generally, Oracle returns a polygon for the extent -- however,
+ # it can return a single point if there's only one Point in the
+ # table.
+ ext_geom = Geometry(clob.read())
+ gtype = str(ext_geom.geom_type)
+ if gtype == 'Polygon':
+ # Construct the 4-tuple from the coordinates in the polygon.
+ shell = ext_geom.shell
+ ll, ur = shell[0][:2], shell[2][:2]
+ elif gtype == 'Point':
+ ll = ext_geom.coords[:2]
+ ur = ll
+ else:
+ raise Exception('Unexpected geometry type returned for extent: %s' % gtype)
+ xmin, ymin = ll
+ xmax, ymax = ur
+ return (xmin, ymin, xmax, ymax)
+ else:
+ return None
+
+ def convert_geom(self, clob, geo_field):
+ if clob:
+ return Geometry(clob.read(), geo_field.srid)
+ else:
+ return None
+
+ def geo_db_type(self, f):
+ """
+ Returns the geometry database type for Oracle. Unlike other spatial
+ backends, no stored procedure is necessary and it's the same for all
+ geometry types.
+ """
+ return 'MDSYS.SDO_GEOMETRY'
+
+ def get_distance(self, f, value, lookup_type):
+ """
+ Returns the distance parameters given the value and the lookup type.
+ On Oracle, geometry columns with a geodetic coordinate system behave
+ implicitly like a geography column, and thus meters will be used as
+ the distance parameter on them.
+ """
+ if not value:
+ return []
+ value = value[0]
+ if isinstance(value, Distance):
+ if f.geodetic(self.connection):
+ dist_param = value.m
+ else:
+ dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection)))
+ else:
+ dist_param = value
+
+ # dwithin lookups on oracle require a special string parameter
+ # that starts with "distance=".
+ if lookup_type == 'dwithin':
+ dist_param = 'distance=%s' % dist_param
+
+ return [dist_param]
+
+ def get_geom_placeholder(self, f, value):
+ """
+ Provides a proper substitution value for Geometries that are not in the
+ SRID of the field. Specifically, this routine will substitute in the
+ SDO_CS.TRANSFORM() function call.
+ """
+ if value is None:
+ return 'NULL'
+
+ def transform_value(val, srid):
+ return val.srid != srid
+
+ if hasattr(value, 'expression'):
+ if transform_value(value, f.srid):
+ placeholder = '%s(%%s, %s)' % (self.transform, f.srid)
+ else:
+ placeholder = '%s'
+ # No geometry value used for F expression, substitue in
+ # the column name instead.
+ return placeholder % self.get_expression_column(value)
+ else:
+ if transform_value(value, f.srid):
+ return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (self.transform, value.srid, f.srid)
+ else:
+ return 'SDO_GEOMETRY(%%s, %s)' % f.srid
+
+ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn):
+ "Returns the SQL WHERE clause for use in Oracle spatial SQL construction."
+ alias, col, db_type = lvalue
+
+ # Getting the quoted table name as `geo_col`.
+ geo_col = '%s.%s' % (qn(alias), qn(col))
+
+ # See if a Oracle Geometry function matches the lookup type next
+ lookup_info = self.geometry_functions.get(lookup_type, False)
+ if lookup_info:
+ # Lookup types that are tuples take tuple arguments, e.g., 'relate' and
+ # 'dwithin' lookup types.
+ if isinstance(lookup_info, tuple):
+ # First element of tuple is lookup type, second element is the type
+ # of the expected argument (e.g., str, float)
+ sdo_op, arg_type = lookup_info
+ geom = value[0]
+
+ # Ensuring that a tuple _value_ was passed in from the user
+ if not isinstance(value, tuple):
+ raise ValueError('Tuple required for `%s` lookup type.' % lookup_type)
+ if len(value) != 2:
+ raise ValueError('2-element tuple required for %s lookup type.' % lookup_type)
+
+ # Ensuring the argument type matches what we expect.
+ if not isinstance(value[1], arg_type):
+ raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1])))
+
+ if lookup_type == 'relate':
+ # The SDORelate class handles construction for these queries,
+ # and verifies the mask argument.
+ return sdo_op(value[1]).as_sql(geo_col, self.get_geom_placeholder(field, geom))
+ else:
+ # Otherwise, just call the `as_sql` method on the SDOOperation instance.
+ return sdo_op.as_sql(geo_col, self.get_geom_placeholder(field, geom))
+ else:
+ # Lookup info is a SDOOperation instance, whose `as_sql` method returns
+ # the SQL necessary for the geometry function call. For example:
+ # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE'
+ return lookup_info.as_sql(geo_col, self.get_geom_placeholder(field, value))
+ elif lookup_type == 'isnull':
+ # Handling 'isnull' lookup type
+ return "%s IS %sNULL" % (geo_col, ('' if value else 'NOT ')), []
+
+ raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
+
+ def spatial_aggregate_sql(self, agg):
+ """
+ Returns the spatial aggregate SQL template and function for the
+ given Aggregate instance.
+ """
+ agg_name = agg.__class__.__name__.lower()
+ if agg_name == 'union':
+ agg_name += 'agg'
+ if agg.is_extent:
+ sql_template = '%(function)s(%(field)s)'
+ else:
+ sql_template = '%(function)s(SDOAGGRTYPE(%(field)s,%(tolerance)s))'
+ sql_function = getattr(self, agg_name)
+ return self.select % sql_template, sql_function
+
+ # Routines for getting the OGC-compliant models.
+ def geometry_columns(self):
+ from django.contrib.gis.db.backends.oracle.models import GeometryColumns
+ return GeometryColumns
+
+ def spatial_ref_sys(self):
+ from django.contrib.gis.db.backends.oracle.models import SpatialRefSys
+ return SpatialRefSys
+
+ def modify_insert_params(self, placeholders, params):
+ """Drop out insert parameters for NULL placeholder. Needed for Oracle Spatial
+ backend due to #10888
+ """
+ # This code doesn't work for bulk insert cases.
+ assert len(placeholders) == 1
+ return [[param for pholder, param
+ in six.moves.zip(placeholders[0], params[0]) if pholder != 'NULL'], ]
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/__init__.py
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/adapter.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/adapter.py
new file mode 100644
index 0000000..8bb514d
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/adapter.py
@@ -0,0 +1,46 @@
+"""
+ This object provides quoting for GEOS geometries into PostgreSQL/PostGIS.
+"""
+from __future__ import unicode_literals
+
+from psycopg2 import Binary
+from psycopg2.extensions import ISQLQuote
+
+class PostGISAdapter(object):
+ def __init__(self, geom):
+ "Initializes on the geometry."
+ # Getting the WKB (in string form, to allow easy pickling of
+ # the adaptor) and the SRID from the geometry.
+ self.ewkb = bytes(geom.ewkb)
+ self.srid = geom.srid
+ self._adapter = Binary(self.ewkb)
+
+ def __conform__(self, proto):
+ # Does the given protocol conform to what Psycopg2 expects?
+ if proto == ISQLQuote:
+ return self
+ else:
+ raise Exception('Error implementing psycopg2 protocol. Is psycopg2 installed?')
+
+ def __eq__(self, other):
+ if not isinstance(other, PostGISAdapter):
+ return False
+ return (self.ewkb == other.ewkb) and (self.srid == other.srid)
+
+ def __str__(self):
+ return self.getquoted()
+
+ def prepare(self, conn):
+ """
+ This method allows escaping the binary in the style required by the
+ server's `standard_conforming_string` setting.
+ """
+ self._adapter.prepare(conn)
+
+ def getquoted(self):
+ "Returns a properly quoted string for use in PostgreSQL/PostGIS."
+ # psycopg will figure out whether to use E'\\000' or '\000'
+ return str('ST_GeomFromEWKB(%s)' % self._adapter.getquoted().decode())
+
+ def prepare_database_save(self, unused):
+ return self
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/base.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/base.py
new file mode 100644
index 0000000..634a7d5
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/base.py
@@ -0,0 +1,12 @@
+from django.db.backends.postgresql_psycopg2.base import *
+from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper as Psycopg2DatabaseWrapper
+from django.contrib.gis.db.backends.postgis.creation import PostGISCreation
+from django.contrib.gis.db.backends.postgis.introspection import PostGISIntrospection
+from django.contrib.gis.db.backends.postgis.operations import PostGISOperations
+
+class DatabaseWrapper(Psycopg2DatabaseWrapper):
+ def __init__(self, *args, **kwargs):
+ super(DatabaseWrapper, self).__init__(*args, **kwargs)
+ self.creation = PostGISCreation(self)
+ self.ops = PostGISOperations(self)
+ self.introspection = PostGISIntrospection(self)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/creation.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/creation.py
new file mode 100644
index 0000000..4f64ecc
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/creation.py
@@ -0,0 +1,95 @@
+from django.conf import settings
+from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation
+from django.utils.functional import cached_property
+
+
+class PostGISCreation(DatabaseCreation):
+ geom_index_type = 'GIST'
+ geom_index_ops = 'GIST_GEOMETRY_OPS'
+ geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND'
+
+ @cached_property
+ def template_postgis(self):
+ template_postgis = getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis')
+ cursor = self.connection.cursor()
+ cursor.execute('SELECT 1 FROM pg_database WHERE datname = %s LIMIT 1;', (template_postgis,))
+ if cursor.fetchone():
+ return template_postgis
+ return None
+
+ def sql_indexes_for_field(self, model, f, style):
+ "Return any spatial index creation SQL for the field."
+ from django.contrib.gis.db.models.fields import GeometryField
+
+ output = super(PostGISCreation, self).sql_indexes_for_field(model, f, style)
+
+ if isinstance(f, GeometryField):
+ gqn = self.connection.ops.geo_quote_name
+ qn = self.connection.ops.quote_name
+ db_table = model._meta.db_table
+
+ if f.geography or self.connection.ops.geometry:
+ # Geography and Geometry (PostGIS 2.0+) columns are
+ # created normally.
+ pass
+ else:
+ # Geometry columns are created by `AddGeometryColumn`
+ # stored procedure.
+ output.append(style.SQL_KEYWORD('SELECT ') +
+ style.SQL_TABLE('AddGeometryColumn') + '(' +
+ style.SQL_TABLE(gqn(db_table)) + ', ' +
+ style.SQL_FIELD(gqn(f.column)) + ', ' +
+ style.SQL_FIELD(str(f.srid)) + ', ' +
+ style.SQL_COLTYPE(gqn(f.geom_type)) + ', ' +
+ style.SQL_KEYWORD(str(f.dim)) + ');')
+
+ if not f.null:
+ # Add a NOT NULL constraint to the field
+ output.append(style.SQL_KEYWORD('ALTER TABLE ') +
+ style.SQL_TABLE(qn(db_table)) +
+ style.SQL_KEYWORD(' ALTER ') +
+ style.SQL_FIELD(qn(f.column)) +
+ style.SQL_KEYWORD(' SET NOT NULL') + ';')
+
+ if f.spatial_index:
+ # Spatial indexes created the same way for both Geometry and
+ # Geography columns.
+ # PostGIS 2.0 does not support GIST_GEOMETRY_OPS. So, on 1.5
+ # we use GIST_GEOMETRY_OPS, on 2.0 we use either "nd" ops
+ # which are fast on multidimensional cases, or just plain
+ # gist index for the 2d case.
+ if f.geography:
+ index_ops = ''
+ elif self.connection.ops.geometry:
+ if f.dim > 2:
+ index_ops = ' ' + style.SQL_KEYWORD(self.geom_index_ops_nd)
+ else:
+ index_ops = ''
+ else:
+ index_ops = ' ' + style.SQL_KEYWORD(self.geom_index_ops)
+ output.append(style.SQL_KEYWORD('CREATE INDEX ') +
+ style.SQL_TABLE(qn('%s_%s_id' % (db_table, f.column))) +
+ style.SQL_KEYWORD(' ON ') +
+ style.SQL_TABLE(qn(db_table)) +
+ style.SQL_KEYWORD(' USING ') +
+ style.SQL_COLTYPE(self.geom_index_type) + ' ( ' +
+ style.SQL_FIELD(qn(f.column)) + index_ops + ' );')
+ return output
+
+ def sql_table_creation_suffix(self):
+ if self.template_postgis is not None:
+ return ' TEMPLATE %s' % (
+ self.connection.ops.quote_name(self.template_postgis),)
+ return ''
+
+ def _create_test_db(self, verbosity, autoclobber):
+ test_database_name = super(PostGISCreation, self)._create_test_db(verbosity, autoclobber)
+ if self.template_postgis is None:
+ # Connect to the test database in order to create the postgis extension
+ self.connection.close()
+ self.connection.settings_dict["NAME"] = test_database_name
+ cursor = self.connection.cursor()
+ cursor.execute("CREATE EXTENSION postgis")
+ cursor.connection.commit()
+
+ return test_database_name
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/introspection.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/introspection.py
new file mode 100644
index 0000000..7df09d0
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/introspection.py
@@ -0,0 +1,103 @@
+from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
+from django.contrib.gis.gdal import OGRGeomType
+
+class GeoIntrospectionError(Exception):
+ pass
+
+class PostGISIntrospection(DatabaseIntrospection):
+ # Reverse dictionary for PostGIS geometry types not populated until
+ # introspection is actually performed.
+ postgis_types_reverse = {}
+
+ ignored_tables = DatabaseIntrospection.ignored_tables + [
+ 'geography_columns',
+ 'geometry_columns',
+ 'raster_columns',
+ 'spatial_ref_sys',
+ 'raster_overviews',
+ ]
+
+ def get_postgis_types(self):
+ """
+ Returns a dictionary with keys that are the PostgreSQL object
+ identification integers for the PostGIS geometry and/or
+ geography types (if supported).
+ """
+ cursor = self.connection.cursor()
+ # The OID integers associated with the geometry type may
+ # be different across versions; hence, this is why we have
+ # to query the PostgreSQL pg_type table corresponding to the
+ # PostGIS custom data types.
+ oid_sql = 'SELECT "oid" FROM "pg_type" WHERE "typname" = %s'
+ try:
+ cursor.execute(oid_sql, ('geometry',))
+ GEOM_TYPE = cursor.fetchone()[0]
+ postgis_types = { GEOM_TYPE : 'GeometryField' }
+ if self.connection.ops.geography:
+ cursor.execute(oid_sql, ('geography',))
+ GEOG_TYPE = cursor.fetchone()[0]
+ # The value for the geography type is actually a tuple
+ # to pass in the `geography=True` keyword to the field
+ # definition.
+ postgis_types[GEOG_TYPE] = ('GeometryField', {'geography' : True})
+ finally:
+ cursor.close()
+
+ return postgis_types
+
+ def get_field_type(self, data_type, description):
+ if not self.postgis_types_reverse:
+ # If the PostGIS types reverse dictionary is not populated, do so
+ # now. In order to prevent unnecessary requests upon connection
+ # intialization, the `data_types_reverse` dictionary is not updated
+ # with the PostGIS custom types until introspection is actually
+ # performed -- in other words, when this function is called.
+ self.postgis_types_reverse = self.get_postgis_types()
+ self.data_types_reverse.update(self.postgis_types_reverse)
+ return super(PostGISIntrospection, self).get_field_type(data_type, description)
+
+ def get_geometry_type(self, table_name, geo_col):
+ """
+ The geometry type OID used by PostGIS does not indicate the particular
+ type of field that a geometry column is (e.g., whether it's a
+ PointField or a PolygonField). Thus, this routine queries the PostGIS
+ metadata tables to determine the geometry type,
+ """
+ cursor = self.connection.cursor()
+ try:
+ try:
+ # First seeing if this geometry column is in the `geometry_columns`
+ cursor.execute('SELECT "coord_dimension", "srid", "type" '
+ 'FROM "geometry_columns" '
+ 'WHERE "f_table_name"=%s AND "f_geometry_column"=%s',
+ (table_name, geo_col))
+ row = cursor.fetchone()
+ if not row: raise GeoIntrospectionError
+ except GeoIntrospectionError:
+ if self.connection.ops.geography:
+ cursor.execute('SELECT "coord_dimension", "srid", "type" '
+ 'FROM "geography_columns" '
+ 'WHERE "f_table_name"=%s AND "f_geography_column"=%s',
+ (table_name, geo_col))
+ row = cursor.fetchone()
+
+ if not row:
+ raise Exception('Could not find a geometry or geography column for "%s"."%s"' %
+ (table_name, geo_col))
+
+ # OGRGeomType does not require GDAL and makes it easy to convert
+ # from OGC geom type name to Django field.
+ field_type = OGRGeomType(row[2]).django
+
+ # Getting any GeometryField keyword arguments that are not the default.
+ dim = row[0]
+ srid = row[1]
+ field_params = {}
+ if srid != 4326:
+ field_params['srid'] = srid
+ if dim != 2:
+ field_params['dim'] = dim
+ finally:
+ cursor.close()
+
+ return field_type, field_params
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/models.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/models.py
new file mode 100644
index 0000000..e805259
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/models.py
@@ -0,0 +1,68 @@
+"""
+ The GeometryColumns and SpatialRefSys models for the PostGIS backend.
+"""
+from django.db import models
+from django.contrib.gis.db.backends.base import SpatialRefSysMixin
+from django.utils.encoding import python_2_unicode_compatible
+
+@python_2_unicode_compatible
+class GeometryColumns(models.Model):
+ """
+ The 'geometry_columns' table from the PostGIS. See the PostGIS
+ documentation at Ch. 4.2.2.
+ """
+ f_table_catalog = models.CharField(max_length=256)
+ f_table_schema = models.CharField(max_length=256)
+ f_table_name = models.CharField(max_length=256)
+ f_geometry_column = models.CharField(max_length=256)
+ coord_dimension = models.IntegerField()
+ srid = models.IntegerField(primary_key=True)
+ type = models.CharField(max_length=30)
+
+ class Meta:
+ db_table = 'geometry_columns'
+ managed = False
+
+ @classmethod
+ def table_name_col(cls):
+ """
+ Returns the name of the metadata column used to store the
+ the feature table name.
+ """
+ return 'f_table_name'
+
+ @classmethod
+ def geom_col_name(cls):
+ """
+ Returns the name of the metadata column used to store the
+ the feature geometry column.
+ """
+ return 'f_geometry_column'
+
+ def __str__(self):
+ return "%s.%s - %dD %s field (SRID: %d)" % \
+ (self.f_table_name, self.f_geometry_column,
+ self.coord_dimension, self.type, self.srid)
+
+class SpatialRefSys(models.Model, SpatialRefSysMixin):
+ """
+ The 'spatial_ref_sys' table from PostGIS. See the PostGIS
+ documentaiton at Ch. 4.2.1.
+ """
+ srid = models.IntegerField(primary_key=True)
+ auth_name = models.CharField(max_length=256)
+ auth_srid = models.IntegerField()
+ srtext = models.CharField(max_length=2048)
+ proj4text = models.CharField(max_length=2048)
+
+ class Meta:
+ db_table = 'spatial_ref_sys'
+ managed = False
+
+ @property
+ def wkt(self):
+ return self.srtext
+
+ @classmethod
+ def wkt_col(cls):
+ return 'srtext'
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/operations.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/operations.py
new file mode 100644
index 0000000..84dbda3
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/operations.py
@@ -0,0 +1,569 @@
+import re
+from decimal import Decimal
+
+from django.conf import settings
+from django.contrib.gis.db.backends.base import BaseSpatialOperations
+from django.contrib.gis.db.backends.util import SpatialOperation, SpatialFunction
+from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter
+from django.contrib.gis.geometry.backend import Geometry
+from django.contrib.gis.measure import Distance
+from django.core.exceptions import ImproperlyConfigured
+from django.db.backends.postgresql_psycopg2.base import DatabaseOperations
+from django.db.utils import DatabaseError
+from django.utils import six
+from django.utils.functional import cached_property
+
+from .models import GeometryColumns, SpatialRefSys
+
+
+#### Classes used in constructing PostGIS spatial SQL ####
+class PostGISOperator(SpatialOperation):
+ "For PostGIS operators (e.g. `&&`, `~`)."
+ def __init__(self, operator):
+ super(PostGISOperator, self).__init__(operator=operator)
+
+class PostGISFunction(SpatialFunction):
+ "For PostGIS function calls (e.g., `ST_Contains(table, geom)`)."
+ def __init__(self, prefix, function, **kwargs):
+ super(PostGISFunction, self).__init__(prefix + function, **kwargs)
+
+class PostGISFunctionParam(PostGISFunction):
+ "For PostGIS functions that take another parameter (e.g. DWithin, Relate)."
+ sql_template = '%(function)s(%(geo_col)s, %(geometry)s, %%s)'
+
+class PostGISDistance(PostGISFunction):
+ "For PostGIS distance operations."
+ dist_func = 'Distance'
+ sql_template = '%(function)s(%(geo_col)s, %(geometry)s) %(operator)s %%s'
+
+ def __init__(self, prefix, operator):
+ super(PostGISDistance, self).__init__(prefix, self.dist_func,
+ operator=operator)
+
+class PostGISSpheroidDistance(PostGISFunction):
+ "For PostGIS spherical distance operations (using the spheroid)."
+ dist_func = 'distance_spheroid'
+ sql_template = '%(function)s(%(geo_col)s, %(geometry)s, %%s) %(operator)s %%s'
+ def __init__(self, prefix, operator):
+ # An extra parameter in `end_subst` is needed for the spheroid string.
+ super(PostGISSpheroidDistance, self).__init__(prefix, self.dist_func,
+ operator=operator)
+
+class PostGISSphereDistance(PostGISDistance):
+ "For PostGIS spherical distance operations."
+ dist_func = 'distance_sphere'
+
+class PostGISRelate(PostGISFunctionParam):
+ "For PostGIS Relate(<geom>, <pattern>) calls."
+ pattern_regex = re.compile(r'^[012TF\*]{9}$')
+ def __init__(self, prefix, pattern):
+ if not self.pattern_regex.match(pattern):
+ raise ValueError('Invalid intersection matrix pattern "%s".' % pattern)
+ super(PostGISRelate, self).__init__(prefix, 'Relate')
+
+
+class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
+ compiler_module = 'django.contrib.gis.db.models.sql.compiler'
+ name = 'postgis'
+ postgis = True
+ geom_func_prefix = 'ST_'
+ version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
+ valid_aggregates = dict([(k, None) for k in
+ ('Collect', 'Extent', 'Extent3D', 'MakeLine', 'Union')])
+
+ Adapter = PostGISAdapter
+ Adaptor = Adapter # Backwards-compatibility alias.
+
+ def __init__(self, connection):
+ super(PostGISOperations, self).__init__(connection)
+
+ prefix = self.geom_func_prefix
+ # PostGIS-specific operators. The commented descriptions of these
+ # operators come from Section 7.6 of the PostGIS 1.4 documentation.
+ self.geometry_operators = {
+ # The "&<" operator returns true if A's bounding box overlaps or
+ # is to the left of B's bounding box.
+ 'overlaps_left' : PostGISOperator('&<'),
+ # The "&>" operator returns true if A's bounding box overlaps or
+ # is to the right of B's bounding box.
+ 'overlaps_right' : PostGISOperator('&>'),
+ # The "<<" operator returns true if A's bounding box is strictly
+ # to the left of B's bounding box.
+ 'left' : PostGISOperator('<<'),
+ # The ">>" operator returns true if A's bounding box is strictly
+ # to the right of B's bounding box.
+ 'right' : PostGISOperator('>>'),
+ # The "&<|" operator returns true if A's bounding box overlaps or
+ # is below B's bounding box.
+ 'overlaps_below' : PostGISOperator('&<|'),
+ # The "|&>" operator returns true if A's bounding box overlaps or
+ # is above B's bounding box.
+ 'overlaps_above' : PostGISOperator('|&>'),
+ # The "<<|" operator returns true if A's bounding box is strictly
+ # below B's bounding box.
+ 'strictly_below' : PostGISOperator('<<|'),
+ # The "|>>" operator returns true if A's bounding box is strictly
+ # above B's bounding box.
+ 'strictly_above' : PostGISOperator('|>>'),
+ # The "~=" operator is the "same as" operator. It tests actual
+ # geometric equality of two features. So if A and B are the same feature,
+ # vertex-by-vertex, the operator returns true.
+ 'same_as' : PostGISOperator('~='),
+ 'exact' : PostGISOperator('~='),
+ # The "@" operator returns true if A's bounding box is completely contained
+ # by B's bounding box.
+ 'contained' : PostGISOperator('@'),
+ # The "~" operator returns true if A's bounding box completely contains
+ # by B's bounding box.
+ 'bbcontains' : PostGISOperator('~'),
+ # The "&&" operator returns true if A's bounding box overlaps
+ # B's bounding box.
+ 'bboverlaps' : PostGISOperator('&&'),
+ }
+
+ self.geometry_functions = {
+ 'equals' : PostGISFunction(prefix, 'Equals'),
+ 'disjoint' : PostGISFunction(prefix, 'Disjoint'),
+ 'touches' : PostGISFunction(prefix, 'Touches'),
+ 'crosses' : PostGISFunction(prefix, 'Crosses'),
+ 'within' : PostGISFunction(prefix, 'Within'),
+ 'overlaps' : PostGISFunction(prefix, 'Overlaps'),
+ 'contains' : PostGISFunction(prefix, 'Contains'),
+ 'intersects' : PostGISFunction(prefix, 'Intersects'),
+ 'relate' : (PostGISRelate, six.string_types),
+ 'coveredby' : PostGISFunction(prefix, 'CoveredBy'),
+ 'covers' : PostGISFunction(prefix, 'Covers'),
+ }
+
+ # Valid distance types and substitutions
+ dtypes = (Decimal, Distance, float) + six.integer_types
+ def get_dist_ops(operator):
+ "Returns operations for both regular and spherical distances."
+ return {'cartesian' : PostGISDistance(prefix, operator),
+ 'sphere' : PostGISSphereDistance(prefix, operator),
+ 'spheroid' : PostGISSpheroidDistance(prefix, operator),
+ }
+ self.distance_functions = {
+ 'distance_gt' : (get_dist_ops('>'), dtypes),
+ 'distance_gte' : (get_dist_ops('>='), dtypes),
+ 'distance_lt' : (get_dist_ops('<'), dtypes),
+ 'distance_lte' : (get_dist_ops('<='), dtypes),
+ 'dwithin' : (PostGISFunctionParam(prefix, 'DWithin'), dtypes)
+ }
+
+ # Adding the distance functions to the geometries lookup.
+ self.geometry_functions.update(self.distance_functions)
+
+ # Only PostGIS versions 1.3.4+ have GeoJSON serialization support.
+ if self.spatial_version < (1, 3, 4):
+ GEOJSON = False
+ else:
+ GEOJSON = prefix + 'AsGeoJson'
+
+ # ST_ContainsProperly ST_MakeLine, and ST_GeoHash added in 1.4.
+ if self.spatial_version >= (1, 4, 0):
+ GEOHASH = 'ST_GeoHash'
+ BOUNDINGCIRCLE = 'ST_MinimumBoundingCircle'
+ self.geometry_functions['contains_properly'] = PostGISFunction(prefix, 'ContainsProperly')
+ else:
+ GEOHASH, BOUNDINGCIRCLE = False, False
+
+ # Geography type support added in 1.5.
+ if self.spatial_version >= (1, 5, 0):
+ self.geography = True
+ # Only a subset of the operators and functions are available
+ # for the geography type.
+ self.geography_functions = self.distance_functions.copy()
+ self.geography_functions.update({
+ 'coveredby': self.geometry_functions['coveredby'],
+ 'covers': self.geometry_functions['covers'],
+ 'intersects': self.geometry_functions['intersects'],
+ })
+ self.geography_operators = {
+ 'bboverlaps': PostGISOperator('&&'),
+ }
+
+ # Native geometry type support added in PostGIS 2.0.
+ if self.spatial_version >= (2, 0, 0):
+ self.geometry = True
+
+ # Creating a dictionary lookup of all GIS terms for PostGIS.
+ self.gis_terms = set(['isnull'])
+ self.gis_terms.update(self.geometry_operators)
+ self.gis_terms.update(self.geometry_functions)
+
+ self.area = prefix + 'Area'
+ self.bounding_circle = BOUNDINGCIRCLE
+ self.centroid = prefix + 'Centroid'
+ self.collect = prefix + 'Collect'
+ self.difference = prefix + 'Difference'
+ self.distance = prefix + 'Distance'
+ self.distance_sphere = prefix + 'distance_sphere'
+ self.distance_spheroid = prefix + 'distance_spheroid'
+ self.envelope = prefix + 'Envelope'
+ self.extent = prefix + 'Extent'
+ self.force_rhr = prefix + 'ForceRHR'
+ self.geohash = GEOHASH
+ self.geojson = GEOJSON
+ self.gml = prefix + 'AsGML'
+ self.intersection = prefix + 'Intersection'
+ self.kml = prefix + 'AsKML'
+ self.length = prefix + 'Length'
+ self.length_spheroid = prefix + 'length_spheroid'
+ self.makeline = prefix + 'MakeLine'
+ self.mem_size = prefix + 'mem_size'
+ self.num_geom = prefix + 'NumGeometries'
+ self.num_points = prefix + 'npoints'
+ self.perimeter = prefix + 'Perimeter'
+ self.point_on_surface = prefix + 'PointOnSurface'
+ self.polygonize = prefix + 'Polygonize'
+ self.reverse = prefix + 'Reverse'
+ self.scale = prefix + 'Scale'
+ self.snap_to_grid = prefix + 'SnapToGrid'
+ self.svg = prefix + 'AsSVG'
+ self.sym_difference = prefix + 'SymDifference'
+ self.transform = prefix + 'Transform'
+ self.translate = prefix + 'Translate'
+ self.union = prefix + 'Union'
+ self.unionagg = prefix + 'Union'
+
+ if self.spatial_version >= (2, 0, 0):
+ self.extent3d = prefix + '3DExtent'
+ self.length3d = prefix + '3DLength'
+ self.perimeter3d = prefix + '3DPerimeter'
+ else:
+ self.extent3d = prefix + 'Extent3D'
+ self.length3d = prefix + 'Length3D'
+ self.perimeter3d = prefix + 'Perimeter3D'
+
+ @cached_property
+ def spatial_version(self):
+ """Determine the version of the PostGIS library."""
+ # Trying to get the PostGIS version because the function
+ # signatures will depend on the version used. The cost
+ # here is a database query to determine the version, which
+ # can be mitigated by setting `POSTGIS_VERSION` with a 3-tuple
+ # comprising user-supplied values for the major, minor, and
+ # subminor revision of PostGIS.
+ if hasattr(settings, 'POSTGIS_VERSION'):
+ version = settings.POSTGIS_VERSION
+ else:
+ try:
+ vtup = self.postgis_version_tuple()
+ except DatabaseError:
+ raise ImproperlyConfigured(
+ 'Cannot determine PostGIS version for database "%s". '
+ 'GeoDjango requires at least PostGIS version 1.3. '
+ 'Was the database created from a spatial database '
+ 'template?' % self.connection.settings_dict['NAME']
+ )
+ version = vtup[1:]
+ return version
+
+ def check_aggregate_support(self, aggregate):
+ """
+ Checks if the given aggregate name is supported (that is, if it's
+ in `self.valid_aggregates`).
+ """
+ agg_name = aggregate.__class__.__name__
+ return agg_name in self.valid_aggregates
+
+ def convert_extent(self, box):
+ """
+ Returns a 4-tuple extent for the `Extent` aggregate by converting
+ the bounding box text returned by PostGIS (`box` argument), for
+ example: "BOX(-90.0 30.0, -85.0 40.0)".
+ """
+ ll, ur = box[4:-1].split(',')
+ xmin, ymin = map(float, ll.split())
+ xmax, ymax = map(float, ur.split())
+ return (xmin, ymin, xmax, ymax)
+
+ def convert_extent3d(self, box3d):
+ """
+ Returns a 6-tuple extent for the `Extent3D` aggregate by converting
+ the 3d bounding-box text returnded by PostGIS (`box3d` argument), for
+ example: "BOX3D(-90.0 30.0 1, -85.0 40.0 2)".
+ """
+ ll, ur = box3d[6:-1].split(',')
+ xmin, ymin, zmin = map(float, ll.split())
+ xmax, ymax, zmax = map(float, ur.split())
+ return (xmin, ymin, zmin, xmax, ymax, zmax)
+
+ def convert_geom(self, hex, geo_field):
+ """
+ Converts the geometry returned from PostGIS aggretates.
+ """
+ if hex:
+ return Geometry(hex)
+ else:
+ return None
+
+ def geo_db_type(self, f):
+ """
+ Return the database field type for the given geometry field.
+ Typically this is `None` because geometry columns are added via
+ the `AddGeometryColumn` stored procedure, unless the field
+ has been specified to be of geography type instead.
+ """
+ if f.geography:
+ if not self.geography:
+ raise NotImplementedError('PostGIS 1.5 required for geography column support.')
+
+ if f.srid != 4326:
+ raise NotImplementedError('PostGIS 1.5 supports geography columns '
+ 'only with an SRID of 4326.')
+
+ return 'geography(%s,%d)' % (f.geom_type, f.srid)
+ elif self.geometry:
+ # Postgis 2.0 supports type-based geometries.
+ # TODO: Support 'M' extension.
+ if f.dim == 3:
+ geom_type = f.geom_type + 'Z'
+ else:
+ geom_type = f.geom_type
+ return 'geometry(%s,%d)' % (geom_type, f.srid)
+ else:
+ return None
+
+ def get_distance(self, f, dist_val, lookup_type):
+ """
+ Retrieve the distance parameters for the given geometry field,
+ distance lookup value, and the distance lookup type.
+
+ This is the most complex implementation of the spatial backends due to
+ what is supported on geodetic geometry columns vs. what's available on
+ projected geometry columns. In addition, it has to take into account
+ the newly introduced geography column type introudced in PostGIS 1.5.
+ """
+ # Getting the distance parameter and any options.
+ if len(dist_val) == 1:
+ value, option = dist_val[0], None
+ else:
+ value, option = dist_val
+
+ # Shorthand boolean flags.
+ geodetic = f.geodetic(self.connection)
+ geography = f.geography and self.geography
+
+ if isinstance(value, Distance):
+ if geography:
+ dist_param = value.m
+ elif geodetic:
+ if lookup_type == 'dwithin':
+ raise ValueError('Only numeric values of degree units are '
+ 'allowed on geographic DWithin queries.')
+ dist_param = value.m
+ else:
+ dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection)))
+ else:
+ # Assuming the distance is in the units of the field.
+ dist_param = value
+
+ if (not geography and geodetic and lookup_type != 'dwithin'
+ and option == 'spheroid'):
+ # using distance_spheroid requires the spheroid of the field as
+ # a parameter.
+ return [f._spheroid, dist_param]
+ else:
+ return [dist_param]
+
+ def get_geom_placeholder(self, f, value):
+ """
+ Provides a proper substitution value for Geometries that are not in the
+ SRID of the field. Specifically, this routine will substitute in the
+ ST_Transform() function call.
+ """
+ if value is None or value.srid == f.srid:
+ placeholder = '%s'
+ else:
+ # Adding Transform() to the SQL placeholder.
+ placeholder = '%s(%%s, %s)' % (self.transform, f.srid)
+
+ if hasattr(value, 'expression'):
+ # If this is an F expression, then we don't really want
+ # a placeholder and instead substitute in the column
+ # of the expression.
+ placeholder = placeholder % self.get_expression_column(value)
+
+ return placeholder
+
+ def _get_postgis_func(self, func):
+ """
+ Helper routine for calling PostGIS functions and returning their result.
+ """
+ # Close out the connection. See #9437.
+ with self.connection.temporary_connection() as cursor:
+ cursor.execute('SELECT %s()' % func)
+ return cursor.fetchone()[0]
+
+ def postgis_geos_version(self):
+ "Returns the version of the GEOS library used with PostGIS."
+ return self._get_postgis_func('postgis_geos_version')
+
+ def postgis_lib_version(self):
+ "Returns the version number of the PostGIS library used with PostgreSQL."
+ return self._get_postgis_func('postgis_lib_version')
+
+ def postgis_proj_version(self):
+ "Returns the version of the PROJ.4 library used with PostGIS."
+ return self._get_postgis_func('postgis_proj_version')
+
+ def postgis_version(self):
+ "Returns PostGIS version number and compile-time options."
+ return self._get_postgis_func('postgis_version')
+
+ def postgis_full_version(self):
+ "Returns PostGIS version number and compile-time options."
+ return self._get_postgis_func('postgis_full_version')
+
+ def postgis_version_tuple(self):
+ """
+ Returns the PostGIS version as a tuple (version string, major,
+ minor, subminor).
+ """
+ # Getting the PostGIS version
+ version = self.postgis_lib_version()
+ m = self.version_regex.match(version)
+
+ if m:
+ major = int(m.group('major'))
+ minor1 = int(m.group('minor1'))
+ minor2 = int(m.group('minor2'))
+ else:
+ raise Exception('Could not parse PostGIS version string: %s' % version)
+
+ return (version, major, minor1, minor2)
+
+ def proj_version_tuple(self):
+ """
+ Return the version of PROJ.4 used by PostGIS as a tuple of the
+ major, minor, and subminor release numbers.
+ """
+ proj_regex = re.compile(r'(\d+)\.(\d+)\.(\d+)')
+ proj_ver_str = self.postgis_proj_version()
+ m = proj_regex.search(proj_ver_str)
+ if m:
+ return tuple(map(int, [m.group(1), m.group(2), m.group(3)]))
+ else:
+ raise Exception('Could not determine PROJ.4 version from PostGIS.')
+
+ def num_params(self, lookup_type, num_param):
+ """
+ Helper routine that returns a boolean indicating whether the number of
+ parameters is correct for the lookup type.
+ """
+ def exactly_two(np): return np == 2
+ def two_to_three(np): return np >= 2 and np <=3
+ if (lookup_type in self.distance_functions and
+ lookup_type != 'dwithin'):
+ return two_to_three(num_param)
+ else:
+ return exactly_two(num_param)
+
+ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn):
+ """
+ Constructs spatial SQL from the given lookup value tuple a
+ (alias, col, db_type), the lookup type string, lookup value, and
+ the geometry field.
+ """
+ alias, col, db_type = lvalue
+
+ # Getting the quoted geometry column.
+ geo_col = '%s.%s' % (qn(alias), qn(col))
+
+ if lookup_type in self.geometry_operators:
+ if field.geography and not lookup_type in self.geography_operators:
+ raise ValueError('PostGIS geography does not support the '
+ '"%s" lookup.' % lookup_type)
+ # Handling a PostGIS operator.
+ op = self.geometry_operators[lookup_type]
+ return op.as_sql(geo_col, self.get_geom_placeholder(field, value))
+ elif lookup_type in self.geometry_functions:
+ if field.geography and not lookup_type in self.geography_functions:
+ raise ValueError('PostGIS geography type does not support the '
+ '"%s" lookup.' % lookup_type)
+
+ # See if a PostGIS geometry function matches the lookup type.
+ tmp = self.geometry_functions[lookup_type]
+
+ # Lookup types that are tuples take tuple arguments, e.g., 'relate' and
+ # distance lookups.
+ if isinstance(tmp, tuple):
+ # First element of tuple is the PostGISOperation instance, and the
+ # second element is either the type or a tuple of acceptable types
+ # that may passed in as further parameters for the lookup type.
+ op, arg_type = tmp
+
+ # Ensuring that a tuple _value_ was passed in from the user
+ if not isinstance(value, (tuple, list)):
+ raise ValueError('Tuple required for `%s` lookup type.' % lookup_type)
+
+ # Geometry is first element of lookup tuple.
+ geom = value[0]
+
+ # Number of valid tuple parameters depends on the lookup type.
+ nparams = len(value)
+ if not self.num_params(lookup_type, nparams):
+ raise ValueError('Incorrect number of parameters given for `%s` lookup type.' % lookup_type)
+
+ # Ensuring the argument type matches what we expect.
+ if not isinstance(value[1], arg_type):
+ raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1])))
+
+ # For lookup type `relate`, the op instance is not yet created (has
+ # to be instantiated here to check the pattern parameter).
+ if lookup_type == 'relate':
+ op = op(self.geom_func_prefix, value[1])
+ elif lookup_type in self.distance_functions and lookup_type != 'dwithin':
+ if not field.geography and field.geodetic(self.connection):
+ # Geodetic distances are only available from Points to
+ # PointFields on PostGIS 1.4 and below.
+ if not self.connection.ops.geography:
+ if field.geom_type != 'POINT':
+ raise ValueError('PostGIS spherical operations are only valid on PointFields.')
+
+ if str(geom.geom_type) != 'Point':
+ raise ValueError('PostGIS geometry distance parameter is required to be of type Point.')
+
+ # Setting up the geodetic operation appropriately.
+ if nparams == 3 and value[2] == 'spheroid':
+ op = op['spheroid']
+ else:
+ op = op['sphere']
+ else:
+ op = op['cartesian']
+ else:
+ op = tmp
+ geom = value
+
+ # Calling the `as_sql` function on the operation instance.
+ return op.as_sql(geo_col, self.get_geom_placeholder(field, geom))
+
+ elif lookup_type == 'isnull':
+ # Handling 'isnull' lookup type
+ return "%s IS %sNULL" % (geo_col, ('' if value else 'NOT ')), []
+
+ raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
+
+ def spatial_aggregate_sql(self, agg):
+ """
+ Returns the spatial aggregate SQL template and function for the
+ given Aggregate instance.
+ """
+ agg_name = agg.__class__.__name__
+ if not self.check_aggregate_support(agg):
+ raise NotImplementedError('%s spatial aggregate is not implmented for this backend.' % agg_name)
+ agg_name = agg_name.lower()
+ if agg_name == 'union':
+ agg_name += 'agg'
+ sql_template = '%(function)s(%(field)s)'
+ sql_function = getattr(self, agg_name)
+ return sql_template, sql_function
+
+ # Routines for getting the OGC-compliant models.
+ def geometry_columns(self):
+ return GeometryColumns
+
+ def spatial_ref_sys(self):
+ return SpatialRefSys
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/__init__.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/__init__.py
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py
new file mode 100644
index 0000000..d8fefba
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/adapter.py
@@ -0,0 +1,8 @@
+from django.db.backends.sqlite3.base import Database
+from django.contrib.gis.db.backends.adapter import WKTAdapter
+
+class SpatiaLiteAdapter(WKTAdapter):
+ "SQLite adaptor for geometry objects."
+ def __conform__(self, protocol):
+ if protocol is Database.PrepareProtocol:
+ return str(self)
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/base.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/base.py
new file mode 100644
index 0000000..7b49d71
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/base.py
@@ -0,0 +1,60 @@
+import sys
+from ctypes.util import find_library
+from django.conf import settings
+
+from django.core.exceptions import ImproperlyConfigured
+from django.db.backends.sqlite3.base import (Database,
+ DatabaseWrapper as SQLiteDatabaseWrapper, SQLiteCursorWrapper)
+from django.contrib.gis.db.backends.spatialite.client import SpatiaLiteClient
+from django.contrib.gis.db.backends.spatialite.creation import SpatiaLiteCreation
+from django.contrib.gis.db.backends.spatialite.introspection import SpatiaLiteIntrospection
+from django.contrib.gis.db.backends.spatialite.operations import SpatiaLiteOperations
+from django.utils import six
+
+class DatabaseWrapper(SQLiteDatabaseWrapper):
+ def __init__(self, *args, **kwargs):
+ # Before we get too far, make sure pysqlite 2.5+ is installed.
+ if Database.version_info < (2, 5, 0):
+ raise ImproperlyConfigured('Only versions of pysqlite 2.5+ are '
+ 'compatible with SpatiaLite and GeoDjango.')
+
+ # Trying to find the location of the SpatiaLite library.
+ # Here we are figuring out the path to the SpatiaLite library
+ # (`libspatialite`). If it's not in the system library path (e.g., it
+ # cannot be found by `ctypes.util.find_library`), then it may be set
+ # manually in the settings via the `SPATIALITE_LIBRARY_PATH` setting.
+ self.spatialite_lib = getattr(settings, 'SPATIALITE_LIBRARY_PATH',
+ find_library('spatialite'))
+ if not self.spatialite_lib:
+ raise ImproperlyConfigured('Unable to locate the SpatiaLite library. '
+ 'Make sure it is in your library path, or set '
+ 'SPATIALITE_LIBRARY_PATH in your settings.'
+ )
+ super(DatabaseWrapper, self).__init__(*args, **kwargs)
+ self.ops = SpatiaLiteOperations(self)
+ self.client = SpatiaLiteClient(self)
+ self.creation = SpatiaLiteCreation(self)
+ self.introspection = SpatiaLiteIntrospection(self)
+
+ def get_new_connection(self, conn_params):
+ conn = super(DatabaseWrapper, self).get_new_connection(conn_params)
+ # Enabling extension loading on the SQLite connection.
+ try:
+ conn.enable_load_extension(True)
+ except AttributeError:
+ raise ImproperlyConfigured(
+ 'The pysqlite library does not support C extension loading. '
+ 'Both SQLite and pysqlite must be configured to allow '
+ 'the loading of extensions to use SpatiaLite.')
+ # Loading the SpatiaLite library extension on the connection, and returning
+ # the created cursor.
+ cur = conn.cursor(factory=SQLiteCursorWrapper)
+ try:
+ cur.execute("SELECT load_extension(%s)", (self.spatialite_lib,))
+ except Exception as msg:
+ new_msg = (
+ 'Unable to load the SpatiaLite library extension '
+ '"%s" because: %s') % (self.spatialite_lib, msg)
+ six.reraise(ImproperlyConfigured, ImproperlyConfigured(new_msg), sys.exc_info()[2])
+ cur.close()
+ return conn
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/client.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/client.py
new file mode 100644
index 0000000..536065a
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/client.py
@@ -0,0 +1,5 @@
+from django.db.backends.sqlite3.client import DatabaseClient
+
+class SpatiaLiteClient(DatabaseClient):
+ executable_name = 'spatialite'
+
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/creation.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/creation.py
new file mode 100644
index 0000000..d0a5f82
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/creation.py
@@ -0,0 +1,129 @@
+import os
+from django.conf import settings
+from django.core.cache import get_cache
+from django.core.cache.backends.db import BaseDatabaseCache
+from django.core.exceptions import ImproperlyConfigured
+from django.db.backends.sqlite3.creation import DatabaseCreation
+
+class SpatiaLiteCreation(DatabaseCreation):
+
+ def create_test_db(self, verbosity=1, autoclobber=False):
+ """
+ Creates a test database, prompting the user for confirmation if the
+ database already exists. Returns the name of the test database created.
+
+ This method is overloaded to load up the SpatiaLite initialization
+ SQL prior to calling the `syncdb` command.
+ """
+ # Don't import django.core.management if it isn't needed.
+ from django.core.management import call_command
+
+ test_database_name = self._get_test_db_name()
+
+ if verbosity >= 1:
+ test_db_repr = ''
+ if verbosity >= 2:
+ test_db_repr = " ('%s')" % test_database_name
+ print("Creating test database for alias '%s'%s..." % (self.connection.alias, test_db_repr))
+
+ self._create_test_db(verbosity, autoclobber)
+
+ self.connection.close()
+ self.connection.settings_dict["NAME"] = test_database_name
+
+ # Need to load the SpatiaLite initialization SQL before running `syncdb`.
+ self.load_spatialite_sql()
+
+ # Report syncdb messages at one level lower than that requested.
+ # This ensures we don't get flooded with messages during testing
+ # (unless you really ask to be flooded)
+ call_command('syncdb',
+ verbosity=max(verbosity - 1, 0),
+ interactive=False,
+ database=self.connection.alias,
+ load_initial_data=False)
+
+ # We need to then do a flush to ensure that any data installed by
+ # custom SQL has been removed. The only test data should come from
+ # test fixtures, or autogenerated from post_syncdb triggers.
+ # This has the side effect of loading initial data (which was
+ # intentionally skipped in the syncdb).
+ call_command('flush',
+ verbosity=max(verbosity - 1, 0),
+ interactive=False,
+ database=self.connection.alias)
+
+ from django.core.cache import get_cache
+ from django.core.cache.backends.db import BaseDatabaseCache
+ for cache_alias in settings.CACHES:
+ cache = get_cache(cache_alias)
+ if isinstance(cache, BaseDatabaseCache):
+ call_command('createcachetable', cache._table, database=self.connection.alias)
+
+ # Get a cursor (even though we don't need one yet). This has
+ # the side effect of initializing the test database.
+ cursor = self.connection.cursor()
+
+ return test_database_name
+
+ def sql_indexes_for_field(self, model, f, style):
+ "Return any spatial index creation SQL for the field."
+ from django.contrib.gis.db.models.fields import GeometryField
+
+ output = super(SpatiaLiteCreation, self).sql_indexes_for_field(model, f, style)
+
+ if isinstance(f, GeometryField):
+ gqn = self.connection.ops.geo_quote_name
+ qn = self.connection.ops.quote_name
+ db_table = model._meta.db_table
+
+ output.append(style.SQL_KEYWORD('SELECT ') +
+ style.SQL_TABLE('AddGeometryColumn') + '(' +
+ style.SQL_TABLE(gqn(db_table)) + ', ' +
+ style.SQL_FIELD(gqn(f.column)) + ', ' +
+ style.SQL_FIELD(str(f.srid)) + ', ' +
+ style.SQL_COLTYPE(gqn(f.geom_type)) + ', ' +
+ style.SQL_KEYWORD(str(f.dim)) + ', ' +
+ style.SQL_KEYWORD(str(int(not f.null))) +
+ ');')
+
+ if f.spatial_index:
+ output.append(style.SQL_KEYWORD('SELECT ') +
+ style.SQL_TABLE('CreateSpatialIndex') + '(' +
+ style.SQL_TABLE(gqn(db_table)) + ', ' +
+ style.SQL_FIELD(gqn(f.column)) + ');')
+
+ return output
+
+ def load_spatialite_sql(self):
+ """
+ This routine loads up the SpatiaLite SQL file.
+ """
+ if self.connection.ops.spatial_version[:2] >= (2, 4):
+ # Spatialite >= 2.4 -- No need to load any SQL file, calling
+ # InitSpatialMetaData() transparently creates the spatial metadata
+ # tables
+ cur = self.connection._cursor()
+ cur.execute("SELECT InitSpatialMetaData()")
+ else:
+ # Spatialite < 2.4 -- Load the initial SQL
+
+ # Getting the location of the SpatiaLite SQL file, and confirming
+ # it exists.
+ spatialite_sql = self.spatialite_init_file()
+ if not os.path.isfile(spatialite_sql):
+ raise ImproperlyConfigured('Could not find the required SpatiaLite initialization '
+ 'SQL file (necessary for testing): %s' % spatialite_sql)
+
+ # Opening up the SpatiaLite SQL initialization file and executing
+ # as a script.
+ with open(spatialite_sql, 'r') as sql_fh:
+ cur = self.connection._cursor()
+ cur.executescript(sql_fh.read())
+
+ def spatialite_init_file(self):
+ # SPATIALITE_SQL may be placed in settings to tell GeoDjango
+ # to use a specific path to the SpatiaLite initilization SQL.
+ return getattr(settings, 'SPATIALITE_SQL',
+ 'init_spatialite-%s.%s.sql' %
+ self.connection.ops.spatial_version[:2])
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py
new file mode 100644
index 0000000..4f12ade
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/introspection.py
@@ -0,0 +1,52 @@
+from django.contrib.gis.gdal import OGRGeomType
+from django.db.backends.sqlite3.introspection import DatabaseIntrospection, FlexibleFieldLookupDict
+from django.utils import six
+
+class GeoFlexibleFieldLookupDict(FlexibleFieldLookupDict):
+ """
+ Sublcass that includes updates the `base_data_types_reverse` dict
+ for geometry field types.
+ """
+ base_data_types_reverse = FlexibleFieldLookupDict.base_data_types_reverse.copy()
+ base_data_types_reverse.update(
+ {'point' : 'GeometryField',
+ 'linestring' : 'GeometryField',
+ 'polygon' : 'GeometryField',
+ 'multipoint' : 'GeometryField',
+ 'multilinestring' : 'GeometryField',
+ 'multipolygon' : 'GeometryField',
+ 'geometrycollection' : 'GeometryField',
+ })
+
+class SpatiaLiteIntrospection(DatabaseIntrospection):
+ data_types_reverse = GeoFlexibleFieldLookupDict()
+
+ def get_geometry_type(self, table_name, geo_col):
+ cursor = self.connection.cursor()
+ try:
+ # Querying the `geometry_columns` table to get additional metadata.
+ cursor.execute('SELECT "coord_dimension", "srid", "type" '
+ 'FROM "geometry_columns" '
+ 'WHERE "f_table_name"=%s AND "f_geometry_column"=%s',
+ (table_name, geo_col))
+ row = cursor.fetchone()
+ if not row:
+ raise Exception('Could not find a geometry column for "%s"."%s"' %
+ (table_name, geo_col))
+
+ # OGRGeomType does not require GDAL and makes it easy to convert
+ # from OGC geom type name to Django field.
+ field_type = OGRGeomType(row[2]).django
+
+ # Getting any GeometryField keyword arguments that are not the default.
+ dim = row[0]
+ srid = row[1]
+ field_params = {}
+ if srid != 4326:
+ field_params['srid'] = srid
+ if isinstance(dim, six.string_types) and 'Z' in dim:
+ field_params['dim'] = 3
+ finally:
+ cursor.close()
+
+ return field_type, field_params
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/models.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/models.py
new file mode 100644
index 0000000..b281f0b
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/models.py
@@ -0,0 +1,62 @@
+"""
+ The GeometryColumns and SpatialRefSys models for the SpatiaLite backend.
+"""
+from django.db import models
+from django.contrib.gis.db.backends.base import SpatialRefSysMixin
+from django.utils.encoding import python_2_unicode_compatible
+
+@python_2_unicode_compatible
+class GeometryColumns(models.Model):
+ """
+ The 'geometry_columns' table from SpatiaLite.
+ """
+ f_table_name = models.CharField(max_length=256)
+ f_geometry_column = models.CharField(max_length=256)
+ type = models.CharField(max_length=30)
+ coord_dimension = models.IntegerField()
+ srid = models.IntegerField(primary_key=True)
+ spatial_index_enabled = models.IntegerField()
+
+ class Meta:
+ db_table = 'geometry_columns'
+ managed = False
+
+ @classmethod
+ def table_name_col(cls):
+ """
+ Returns the name of the metadata column used to store the
+ the feature table name.
+ """
+ return 'f_table_name'
+
+ @classmethod
+ def geom_col_name(cls):
+ """
+ Returns the name of the metadata column used to store the
+ the feature geometry column.
+ """
+ return 'f_geometry_column'
+
+ def __str__(self):
+ return "%s.%s - %dD %s field (SRID: %d)" % \
+ (self.f_table_name, self.f_geometry_column,
+ self.coord_dimension, self.type, self.srid)
+
+class SpatialRefSys(models.Model, SpatialRefSysMixin):
+ """
+ The 'spatial_ref_sys' table from SpatiaLite.
+ """
+ srid = models.IntegerField(primary_key=True)
+ auth_name = models.CharField(max_length=256)
+ auth_srid = models.IntegerField()
+ ref_sys_name = models.CharField(max_length=256)
+ proj4text = models.CharField(max_length=2048)
+
+ @property
+ def wkt(self):
+ from django.contrib.gis.gdal import SpatialReference
+ return SpatialReference(self.proj4text).wkt
+
+ class Meta:
+ db_table = 'spatial_ref_sys'
+ managed = False
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/operations.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/operations.py
new file mode 100644
index 0000000..4281caf
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/operations.py
@@ -0,0 +1,373 @@
+import re
+import sys
+from decimal import Decimal
+
+from django.contrib.gis.db.backends.base import BaseSpatialOperations
+from django.contrib.gis.db.backends.util import SpatialOperation, SpatialFunction
+from django.contrib.gis.db.backends.spatialite.adapter import SpatiaLiteAdapter
+from django.contrib.gis.geometry.backend import Geometry
+from django.contrib.gis.measure import Distance
+from django.core.exceptions import ImproperlyConfigured
+from django.db.backends.sqlite3.base import DatabaseOperations
+from django.db.utils import DatabaseError
+from django.utils import six
+from django.utils.functional import cached_property
+
+
+class SpatiaLiteOperator(SpatialOperation):
+ "For SpatiaLite operators (e.g. `&&`, `~`)."
+ def __init__(self, operator):
+ super(SpatiaLiteOperator, self).__init__(operator=operator)
+
+class SpatiaLiteFunction(SpatialFunction):
+ "For SpatiaLite function calls."
+ def __init__(self, function, **kwargs):
+ super(SpatiaLiteFunction, self).__init__(function, **kwargs)
+
+class SpatiaLiteFunctionParam(SpatiaLiteFunction):
+ "For SpatiaLite functions that take another parameter."
+ sql_template = '%(function)s(%(geo_col)s, %(geometry)s, %%s)'
+
+class SpatiaLiteDistance(SpatiaLiteFunction):
+ "For SpatiaLite distance operations."
+ dist_func = 'Distance'
+ sql_template = '%(function)s(%(geo_col)s, %(geometry)s) %(operator)s %%s'
+
+ def __init__(self, operator):
+ super(SpatiaLiteDistance, self).__init__(self.dist_func,
+ operator=operator)
+
+class SpatiaLiteRelate(SpatiaLiteFunctionParam):
+ "For SpatiaLite Relate(<geom>, <pattern>) calls."
+ pattern_regex = re.compile(r'^[012TF\*]{9}$')
+ def __init__(self, pattern):
+ if not self.pattern_regex.match(pattern):
+ raise ValueError('Invalid intersection matrix pattern "%s".' % pattern)
+ super(SpatiaLiteRelate, self).__init__('Relate')
+
+# Valid distance types and substitutions
+dtypes = (Decimal, Distance, float) + six.integer_types
+def get_dist_ops(operator):
+ "Returns operations for regular distances; spherical distances are not currently supported."
+ return (SpatiaLiteDistance(operator),)
+
+class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
+ compiler_module = 'django.contrib.gis.db.models.sql.compiler'
+ name = 'spatialite'
+ spatialite = True
+ version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
+ valid_aggregates = dict([(k, None) for k in ('Extent', 'Union')])
+
+ Adapter = SpatiaLiteAdapter
+ Adaptor = Adapter # Backwards-compatibility alias.
+
+ area = 'Area'
+ centroid = 'Centroid'
+ contained = 'MbrWithin'
+ difference = 'Difference'
+ distance = 'Distance'
+ envelope = 'Envelope'
+ intersection = 'Intersection'
+ length = 'GLength' # OpenGis defines Length, but this conflicts with an SQLite reserved keyword
+ num_geom = 'NumGeometries'
+ num_points = 'NumPoints'
+ point_on_surface = 'PointOnSurface'
+ scale = 'ScaleCoords'
+ svg = 'AsSVG'
+ sym_difference = 'SymDifference'
+ transform = 'Transform'
+ translate = 'ShiftCoords'
+ union = 'GUnion' # OpenGis defines Union, but this conflicts with an SQLite reserved keyword
+ unionagg = 'GUnion'
+
+ from_text = 'GeomFromText'
+ from_wkb = 'GeomFromWKB'
+ select = 'AsText(%s)'
+
+ geometry_functions = {
+ 'equals' : SpatiaLiteFunction('Equals'),
+ 'disjoint' : SpatiaLiteFunction('Disjoint'),
+ 'touches' : SpatiaLiteFunction('Touches'),
+ 'crosses' : SpatiaLiteFunction('Crosses'),
+ 'within' : SpatiaLiteFunction('Within'),
+ 'overlaps' : SpatiaLiteFunction('Overlaps'),
+ 'contains' : SpatiaLiteFunction('Contains'),
+ 'intersects' : SpatiaLiteFunction('Intersects'),
+ 'relate' : (SpatiaLiteRelate, six.string_types),
+ # Returns true if B's bounding box completely contains A's bounding box.
+ 'contained' : SpatiaLiteFunction('MbrWithin'),
+ # Returns true if A's bounding box completely contains B's bounding box.
+ 'bbcontains' : SpatiaLiteFunction('MbrContains'),
+ # Returns true if A's bounding box overlaps B's bounding box.
+ 'bboverlaps' : SpatiaLiteFunction('MbrOverlaps'),
+ # These are implemented here as synonyms for Equals
+ 'same_as' : SpatiaLiteFunction('Equals'),
+ 'exact' : SpatiaLiteFunction('Equals'),
+ }
+
+ distance_functions = {
+ 'distance_gt' : (get_dist_ops('>'), dtypes),
+ 'distance_gte' : (get_dist_ops('>='), dtypes),
+ 'distance_lt' : (get_dist_ops('<'), dtypes),
+ 'distance_lte' : (get_dist_ops('<='), dtypes),
+ }
+ geometry_functions.update(distance_functions)
+
+ def __init__(self, connection):
+ super(DatabaseOperations, self).__init__(connection)
+
+ # Creating the GIS terms dictionary.
+ self.gis_terms = set(['isnull'])
+ self.gis_terms.update(self.geometry_functions)
+
+ @cached_property
+ def spatial_version(self):
+ """Determine the version of the SpatiaLite library."""
+ try:
+ version = self.spatialite_version_tuple()[1:]
+ except Exception as msg:
+ new_msg = (
+ 'Cannot determine the SpatiaLite version for the "%s" '
+ 'database (error was "%s"). Was the SpatiaLite initialization '
+ 'SQL loaded on this database?') % (self.connection.settings_dict['NAME'], msg)
+ six.reraise(ImproperlyConfigured, ImproperlyConfigured(new_msg), sys.exc_info()[2])
+ if version < (2, 3, 0):
+ raise ImproperlyConfigured('GeoDjango only supports SpatiaLite versions '
+ '2.3.0 and above')
+ return version
+
+ @property
+ def _version_greater_2_4_0_rc4(self):
+ if self.spatial_version >= (2, 4, 1):
+ return True
+ elif self.spatial_version < (2, 4, 0):
+ return False
+ else:
+ # Spatialite 2.4.0-RC4 added AsGML and AsKML, however both
+ # RC2 (shipped in popular Debian/Ubuntu packages) and RC4
+ # report version as '2.4.0', so we fall back to feature detection
+ try:
+ self._get_spatialite_func("AsGML(GeomFromText('POINT(1 1)'))")
+ except DatabaseError:
+ return False
+ return True
+
+ @cached_property
+ def gml(self):
+ return 'AsGML' if self._version_greater_2_4_0_rc4 else None
+
+ @cached_property
+ def kml(self):
+ return 'AsKML' if self._version_greater_2_4_0_rc4 else None
+
+ @cached_property
+ def geojson(self):
+ return 'AsGeoJSON' if self.spatial_version >= (3, 0, 0) else None
+
+ def check_aggregate_support(self, aggregate):
+ """
+ Checks if the given aggregate name is supported (that is, if it's
+ in `self.valid_aggregates`).
+ """
+ agg_name = aggregate.__class__.__name__
+ return agg_name in self.valid_aggregates
+
+ def convert_geom(self, wkt, geo_field):
+ """
+ Converts geometry WKT returned from a SpatiaLite aggregate.
+ """
+ if wkt:
+ return Geometry(wkt, geo_field.srid)
+ else:
+ return None
+
+ def geo_db_type(self, f):
+ """
+ Returns None because geometry columnas are added via the
+ `AddGeometryColumn` stored procedure on SpatiaLite.
+ """
+ return None
+
+ def get_distance(self, f, value, lookup_type):
+ """
+ Returns the distance parameters for the given geometry field,
+ lookup value, and lookup type. SpatiaLite only supports regular
+ cartesian-based queries (no spheroid/sphere calculations for point
+ geometries like PostGIS).
+ """
+ if not value:
+ return []
+ value = value[0]
+ if isinstance(value, Distance):
+ if f.geodetic(self.connection):
+ raise ValueError('SpatiaLite does not support distance queries on '
+ 'geometry fields with a geodetic coordinate system. '
+ 'Distance objects; use a numeric value of your '
+ 'distance in degrees instead.')
+ else:
+ dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection)))
+ else:
+ dist_param = value
+ return [dist_param]
+
+ def get_geom_placeholder(self, f, value):
+ """
+ Provides a proper substitution value for Geometries that are not in the
+ SRID of the field. Specifically, this routine will substitute in the
+ Transform() and GeomFromText() function call(s).
+ """
+ def transform_value(value, srid):
+ return not (value is None or value.srid == srid)
+ if hasattr(value, 'expression'):
+ if transform_value(value, f.srid):
+ placeholder = '%s(%%s, %s)' % (self.transform, f.srid)
+ else:
+ placeholder = '%s'
+ # No geometry value used for F expression, substitue in
+ # the column name instead.
+ return placeholder % self.get_expression_column(value)
+ else:
+ if transform_value(value, f.srid):
+ # Adding Transform() to the SQL placeholder.
+ return '%s(%s(%%s,%s), %s)' % (self.transform, self.from_text, value.srid, f.srid)
+ else:
+ return '%s(%%s,%s)' % (self.from_text, f.srid)
+
+ def _get_spatialite_func(self, func):
+ """
+ Helper routine for calling SpatiaLite functions and returning
+ their result.
+ """
+ cursor = self.connection._cursor()
+ try:
+ try:
+ cursor.execute('SELECT %s' % func)
+ row = cursor.fetchone()
+ except:
+ # Responsibility of caller to perform error handling.
+ raise
+ finally:
+ cursor.close()
+ return row[0]
+
+ def geos_version(self):
+ "Returns the version of GEOS used by SpatiaLite as a string."
+ return self._get_spatialite_func('geos_version()')
+
+ def proj4_version(self):
+ "Returns the version of the PROJ.4 library used by SpatiaLite."
+ return self._get_spatialite_func('proj4_version()')
+
+ def spatialite_version(self):
+ "Returns the SpatiaLite library version as a string."
+ return self._get_spatialite_func('spatialite_version()')
+
+ def spatialite_version_tuple(self):
+ """
+ Returns the SpatiaLite version as a tuple (version string, major,
+ minor, subminor).
+ """
+ # Getting the SpatiaLite version.
+ try:
+ version = self.spatialite_version()
+ except DatabaseError:
+ # The `spatialite_version` function first appeared in version 2.3.1
+ # of SpatiaLite, so doing a fallback test for 2.3.0 (which is
+ # used by popular Debian/Ubuntu packages).
+ version = None
+ try:
+ tmp = self._get_spatialite_func("X(GeomFromText('POINT(1 1)'))")
+ if tmp == 1.0: version = '2.3.0'
+ except DatabaseError:
+ pass
+ # If no version string defined, then just re-raise the original
+ # exception.
+ if version is None: raise
+
+ m = self.version_regex.match(version)
+ if m:
+ major = int(m.group('major'))
+ minor1 = int(m.group('minor1'))
+ minor2 = int(m.group('minor2'))
+ else:
+ raise Exception('Could not parse SpatiaLite version string: %s' % version)
+
+ return (version, major, minor1, minor2)
+
+ def spatial_aggregate_sql(self, agg):
+ """
+ Returns the spatial aggregate SQL template and function for the
+ given Aggregate instance.
+ """
+ agg_name = agg.__class__.__name__
+ if not self.check_aggregate_support(agg):
+ raise NotImplementedError('%s spatial aggregate is not implmented for this backend.' % agg_name)
+ agg_name = agg_name.lower()
+ if agg_name == 'union': agg_name += 'agg'
+ sql_template = self.select % '%(function)s(%(field)s)'
+ sql_function = getattr(self, agg_name)
+ return sql_template, sql_function
+
+ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn):
+ """
+ Returns the SpatiaLite-specific SQL for the given lookup value
+ [a tuple of (alias, column, db_type)], lookup type, lookup
+ value, the model field, and the quoting function.
+ """
+ alias, col, db_type = lvalue
+
+ # Getting the quoted field as `geo_col`.
+ geo_col = '%s.%s' % (qn(alias), qn(col))
+
+ if lookup_type in self.geometry_functions:
+ # See if a SpatiaLite geometry function matches the lookup type.
+ tmp = self.geometry_functions[lookup_type]
+
+ # Lookup types that are tuples take tuple arguments, e.g., 'relate' and
+ # distance lookups.
+ if isinstance(tmp, tuple):
+ # First element of tuple is the SpatiaLiteOperation instance, and the
+ # second element is either the type or a tuple of acceptable types
+ # that may passed in as further parameters for the lookup type.
+ op, arg_type = tmp
+
+ # Ensuring that a tuple _value_ was passed in from the user
+ if not isinstance(value, (tuple, list)):
+ raise ValueError('Tuple required for `%s` lookup type.' % lookup_type)
+
+ # Geometry is first element of lookup tuple.
+ geom = value[0]
+
+ # Number of valid tuple parameters depends on the lookup type.
+ if len(value) != 2:
+ raise ValueError('Incorrect number of parameters given for `%s` lookup type.' % lookup_type)
+
+ # Ensuring the argument type matches what we expect.
+ if not isinstance(value[1], arg_type):
+ raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1])))
+
+ # For lookup type `relate`, the op instance is not yet created (has
+ # to be instantiated here to check the pattern parameter).
+ if lookup_type == 'relate':
+ op = op(value[1])
+ elif lookup_type in self.distance_functions:
+ op = op[0]
+ else:
+ op = tmp
+ geom = value
+ # Calling the `as_sql` function on the operation instance.
+ return op.as_sql(geo_col, self.get_geom_placeholder(field, geom))
+ elif lookup_type == 'isnull':
+ # Handling 'isnull' lookup type
+ return "%s IS %sNULL" % (geo_col, ('' if value else 'NOT ')), []
+
+ raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
+
+ # Routines for getting the OGC-compliant models.
+ def geometry_columns(self):
+ from django.contrib.gis.db.backends.spatialite.models import GeometryColumns
+ return GeometryColumns
+
+ def spatial_ref_sys(self):
+ from django.contrib.gis.db.backends.spatialite.models import SpatialRefSys
+ return SpatialRefSys
diff --git a/lib/python2.7/site-packages/django/contrib/gis/db/backends/util.py b/lib/python2.7/site-packages/django/contrib/gis/db/backends/util.py
new file mode 100644
index 0000000..2612810
--- /dev/null
+++ b/lib/python2.7/site-packages/django/contrib/gis/db/backends/util.py
@@ -0,0 +1,44 @@
+"""
+A collection of utility routines and classes used by the spatial
+backends.
+"""
+
+class SpatialOperation(object):
+ """
+ Base class for generating spatial SQL.
+ """
+ sql_template = '%(geo_col)s %(operator)s %(geometry)s'
+
+ def __init__(self, function='', operator='', result='', **kwargs):
+ self.function = function
+ self.operator = operator
+ self.result = result
+ self.extra = kwargs
+
+ def as_sql(self, geo_col, geometry='%s'):
+ return self.sql_template % self.params(geo_col, geometry), []
+
+ def params(self, geo_col, geometry):
+ params = {'function' : self.function,
+ 'geo_col' : geo_col,
+ 'geometry' : geometry,
+ 'operator' : self.operator,
+ 'result' : self.result,
+ }
+ params.update(self.extra)
+ return params
+
+class SpatialFunction(SpatialOperation):
+ """
+ Base class for generating spatial SQL related to a function.
+ """
+ sql_template = '%(function)s(%(geo_col)s, %(geometry)s)'
+
+ def __init__(self, func, result='', operator='', **kwargs):
+ # Getting the function prefix.
+ default = {'function' : func,
+ 'operator' : operator,
+ 'result' : result
+ }
+ kwargs.update(default)
+ super(SpatialFunction, self).__init__(**kwargs)