mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
gis: Added preliminary spatial backend for Oracle; added GEOS routine fromfile
.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6524 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
189335acaa
commit
ba9fa9844c
@ -35,8 +35,16 @@ if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
|
||||
PostGISField as GeoBackendField, POSTGIS_TERMS as GIS_TERMS, \
|
||||
create_spatial_db, get_geo_where_clause, gqn, \
|
||||
ASGML, ASKML, GEOM_SELECT, TRANSFORM, UNION
|
||||
SPATIAL_BACKEND = 'postgis'
|
||||
elif settings.DATABASE_ENGINE == 'oracle':
|
||||
from django.contrib.gis.db.backend.oracle import \
|
||||
OracleSpatialField as GeoBackendField, \
|
||||
ORACLE_SPATIAL_TERMS as GIS_TERMS, \
|
||||
create_spatial_db, get_geo_where_clause, gqn, \
|
||||
ASGML, GEOM_SELECT, TRANSFORM, UNION
|
||||
SPATIAL_BACKEND = 'oracle'
|
||||
else:
|
||||
raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_NAME)
|
||||
raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE)
|
||||
|
||||
def geo_quotename(value):
|
||||
"""
|
||||
|
12
django/contrib/gis/db/backend/oracle/__init__.py
Normal file
12
django/contrib/gis/db/backend/oracle/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""
|
||||
The Oracle spatial database backend module.
|
||||
|
||||
Please note that WKT support is broken on the XE version, and this will
|
||||
not work.
|
||||
"""
|
||||
from django.contrib.gis.db.backend.oracle.creation import create_spatial_db
|
||||
from django.contrib.gis.db.backend.oracle.field import OracleSpatialField, gqn
|
||||
from django.contrib.gis.db.backend.oracle.query import \
|
||||
get_geo_where_clause, ORACLE_SPATIAL_TERMS, \
|
||||
ASGML, GEOM_SELECT, TRANSFORM, UNION
|
||||
|
21
django/contrib/gis/db/backend/oracle/adaptor.py
Normal file
21
django/contrib/gis/db/backend/oracle/adaptor.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""
|
||||
This object provides the database adaptor for Oracle geometries.
|
||||
"""
|
||||
from cx_Oracle import CLOB
|
||||
|
||||
class OracleSpatialAdaptor(object):
|
||||
def __init__(self, geom):
|
||||
"Initializes only on the geometry object."
|
||||
self.wkt = geom.wkt
|
||||
|
||||
def __str__(self):
|
||||
"WKT is used for the substitution value of the geometry."
|
||||
return self.wkt
|
||||
|
||||
def oracle_type(self):
|
||||
"""
|
||||
The parameter type is a CLOB because no string (VARCHAR2) greater
|
||||
than 4000 characters will be accepted through the Oracle database
|
||||
API and/or SQL*Plus.
|
||||
"""
|
||||
return CLOB
|
8
django/contrib/gis/db/backend/oracle/creation.py
Normal file
8
django/contrib/gis/db/backend/oracle/creation.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.db.backends.oracle.creation import create_test_db
|
||||
|
||||
def create_spatial_db(test=True, verbosity=1, autoclobber=False):
|
||||
"A wrapper over the Oracle `create_test_db` routine."
|
||||
if not test: raise NotImplementedError('This uses `create_test_db` from db/backends/oracle/creation.py')
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
create_test_db(settings, connection, verbosity, autoclobber)
|
159
django/contrib/gis/db/backend/oracle/field.py
Normal file
159
django/contrib/gis/db/backend/oracle/field.py
Normal file
@ -0,0 +1,159 @@
|
||||
import re
|
||||
from types import StringType, UnicodeType
|
||||
from django.db import connection
|
||||
from django.db.backends.util import truncate_name
|
||||
from django.db.models.fields import Field # Django base Field class
|
||||
from django.contrib.gis.geos import GEOSGeometry
|
||||
from django.contrib.gis.db.backend.util import GeoFieldSQL
|
||||
from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor
|
||||
from django.contrib.gis.db.backend.oracle.query import ORACLE_SPATIAL_TERMS, TRANSFORM
|
||||
|
||||
# Quotename & geographic quotename, respectively.
|
||||
qn = connection.ops.quote_name
|
||||
def gqn(value):
|
||||
if isinstance(value, UnicodeType): value = value.encode('ascii')
|
||||
return "'%s'" % value
|
||||
|
||||
class OracleSpatialField(Field):
|
||||
"""
|
||||
The backend-specific geographic field for Oracle Spatial.
|
||||
"""
|
||||
|
||||
empty_strings_allowed = False
|
||||
|
||||
def __init__(self, extent=(-180.0, -90.0, 180.0, 90.0), tolerance=0.00005, **kwargs):
|
||||
"""
|
||||
Oracle Spatial backend needs to have the extent -- for projected coordinate
|
||||
systems _you must define the extent manually_, since the coordinates are
|
||||
for geodetic systems. The `tolerance` keyword specifies the tolerance
|
||||
for error (in meters).
|
||||
"""
|
||||
# Oracle Spatial specific keyword arguments.
|
||||
self._extent = extent
|
||||
self._tolerance = tolerance
|
||||
# Calling the Django field initialization.
|
||||
super(OracleSpatialField, self).__init__(**kwargs)
|
||||
|
||||
def _add_geom(self, style, db_table):
|
||||
"""
|
||||
Adds this geometry column into the Oracle USER_SDO_GEOM_METADATA
|
||||
table.
|
||||
"""
|
||||
|
||||
# Checking the dimensions.
|
||||
# TODO: Add support for 3D geometries.
|
||||
if self._dim != 2:
|
||||
raise Exception('3D geometries not yet supported on Oracle Spatial backend.')
|
||||
|
||||
# Constructing the SQL that will be used to insert information about
|
||||
# the geometry column into the USER_GSDO_GEOM_METADATA table.
|
||||
meta_sql = 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(self.column)) + ',\n ' + \
|
||||
style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' + \
|
||||
style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
|
||||
("('LONG', %s, %s, %s),\n " % (self._extent[0], self._extent[2], self._tolerance)) + \
|
||||
style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
|
||||
("('LAT', %s, %s, %s)\n ),\n" % (self._extent[1], self._extent[3], self._tolerance)) + \
|
||||
' %s\n );' % self._srid
|
||||
return meta_sql
|
||||
|
||||
def _geom_index(self, style, db_table):
|
||||
"Creates an Oracle Geometry index (R-tree) for this geometry field."
|
||||
|
||||
# Getting the index name, Oracle doesn't allow object
|
||||
# names > 30 characters.
|
||||
idx_name = truncate_name('%s_%s_id' % (db_table, self.column), 30)
|
||||
|
||||
sql = 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(self.column)) + ') ' + \
|
||||
style.SQL_KEYWORD('INDEXTYPE IS ') + \
|
||||
style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';'
|
||||
return sql
|
||||
|
||||
def _post_create_sql(self, style, db_table):
|
||||
"""
|
||||
Returns SQL that will be executed after the model has been
|
||||
created.
|
||||
"""
|
||||
# Getting the meta geometry information.
|
||||
post_sql = self._add_geom(style, db_table)
|
||||
|
||||
# Getting the geometric index for this Geometry column.
|
||||
if self._index:
|
||||
return (post_sql, self._geom_index(style, db_table))
|
||||
else:
|
||||
return (post_sql,)
|
||||
|
||||
def db_type(self):
|
||||
"The Oracle geometric data type is MDSYS.SDO_GEOMETRY."
|
||||
return 'MDSYS.SDO_GEOMETRY'
|
||||
|
||||
def get_db_prep_lookup(self, lookup_type, value):
|
||||
"""
|
||||
Returns field's value prepared for database lookup, accepts WKT and
|
||||
GEOS Geometries for the value.
|
||||
"""
|
||||
if lookup_type in ORACLE_SPATIAL_TERMS:
|
||||
# special case for isnull lookup
|
||||
if lookup_type == 'isnull': return GeoFieldSQL([], [])
|
||||
|
||||
# When the input is not a GEOS geometry, attempt to construct one
|
||||
# from the given string input.
|
||||
if isinstance(value, GEOSGeometry):
|
||||
pass
|
||||
elif isinstance(value, (StringType, UnicodeType)):
|
||||
try:
|
||||
value = GEOSGeometry(value)
|
||||
except GEOSException:
|
||||
raise TypeError("Could not create geometry from lookup value: %s" % str(value))
|
||||
else:
|
||||
raise TypeError('Cannot use parameter of %s type as lookup parameter.' % type(value))
|
||||
|
||||
# Getting the SRID of the geometry, or defaulting to that of the field if
|
||||
# it is None.
|
||||
if value.srid is None: srid = self._srid
|
||||
else: srid = value.srid
|
||||
|
||||
# The adaptor will be used by psycopg2 for quoting the WKT.
|
||||
adapt = OracleSpatialAdaptor(value)
|
||||
if srid != self._srid:
|
||||
# Adding the necessary string substitutions and parameters
|
||||
# to perform a geometry transformation.
|
||||
return GeoFieldSQL(['%s(SDO_GEOMETRY(%%s, %s), %%s)' % (TRANSFORM, srid)],
|
||||
[adapt, self._srid])
|
||||
else:
|
||||
return GeoFieldSQL(['SDO_GEOMETRY(%%s, %s)' % srid], [adapt])
|
||||
|
||||
else:
|
||||
raise TypeError("Field has invalid lookup: %s" % lookup_type)
|
||||
|
||||
def get_db_prep_save(self, value):
|
||||
"Prepares the value for saving in the database."
|
||||
if not bool(value):
|
||||
# Return an empty string for NULL -- but this doesn't work yet.
|
||||
return ''
|
||||
if isinstance(value, GEOSGeometry):
|
||||
return OracleSpatialAdaptor(value)
|
||||
else:
|
||||
raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')
|
||||
|
||||
def get_placeholder(self, 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 isinstance(value, GEOSGeometry) and value.srid != self._srid:
|
||||
# Adding Transform() to the SQL placeholder.
|
||||
return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self._srid)
|
||||
elif value is None:
|
||||
return '%s'
|
||||
else:
|
||||
return 'SDO_GEOMETRY(%%s, %s)' % self._srid
|
45
django/contrib/gis/db/backend/oracle/models.py
Normal file
45
django/contrib/gis/db/backend/oracle/models.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
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.db import models
|
||||
from django.contrib.gis.models import SpatialRefSysMixin
|
||||
|
||||
class GeometryColumns(models.Model):
|
||||
"Maps to the Oracle USER_SDO_GEOM_METADATA table."
|
||||
table_name = models.CharField(maxlength=32)
|
||||
column_name = models.CharField(maxlength=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'
|
||||
|
||||
@classmethod
|
||||
def table_name_col(self):
|
||||
return 'table_name'
|
||||
|
||||
def __unicode__(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(maxlength=68)
|
||||
srid = models.IntegerField(primary_key=True)
|
||||
auth_srid = models.IntegerField()
|
||||
auth_name = models.CharField(maxlength=256)
|
||||
wktext = models.CharField(maxlength=2046)
|
||||
#cs_bounds = models.GeometryField()
|
||||
|
||||
class Meta:
|
||||
# TODO: Figure out way to have this be MDSYS.CS_SRS without
|
||||
# having django's quoting mess up the SQL.
|
||||
db_table = 'CS_SRS'
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
return self.wktext
|
78
django/contrib/gis/db/backend/oracle/query.py
Normal file
78
django/contrib/gis/db/backend/oracle/query.py
Normal file
@ -0,0 +1,78 @@
|
||||
"""
|
||||
This module contains the spatial lookup types, and the get_geo_where_clause()
|
||||
routine for Oracle Spatial.
|
||||
"""
|
||||
from django.db import connection
|
||||
qn = connection.ops.quote_name
|
||||
|
||||
ORACLE_GEOMETRY_FUNCTIONS = {
|
||||
'contains' : 'SDO_CONTAINS',
|
||||
'coveredby' : 'SDO_COVEREDBY',
|
||||
'covers' : 'SDO_COVERS',
|
||||
'disjoint' : 'SDO_DISJOINT',
|
||||
'dwithin' : ('SDO_WITHIN_DISTANCE', float),
|
||||
'intersects' : 'SDO_OVERLAPBDYINTERSECT', # TODO: Is this really the same as ST_Intersects()?
|
||||
'equals' : 'SDO_EQUAL',
|
||||
'exact' : 'SDO_EQUAL',
|
||||
'overlaps' : 'SDO_OVERLAPS',
|
||||
'same_as' : 'SDO_EQUAL',
|
||||
#'relate' : ('SDO_RELATE', str), # Oracle uses a different syntax, e.g., 'mask=inside+touch'
|
||||
'touches' : 'SDO_TOUCH',
|
||||
'within' : 'SDO_INSIDE',
|
||||
}
|
||||
|
||||
# This lookup type does not require a mapping.
|
||||
MISC_TERMS = ['isnull']
|
||||
|
||||
# Assacceptable lookup types for Oracle spatial.
|
||||
ORACLE_SPATIAL_TERMS = ORACLE_GEOMETRY_FUNCTIONS.keys()
|
||||
ORACLE_SPATIAL_TERMS += MISC_TERMS
|
||||
ORACLE_SPATIAL_TERMS = tuple(ORACLE_SPATIAL_TERMS) # Making immutable
|
||||
|
||||
def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
|
||||
"Returns the SQL WHERE clause for use in Oracle spatial SQL construction."
|
||||
if table_prefix.endswith('.'):
|
||||
table_prefix = qn(table_prefix[:-1])+'.'
|
||||
field_name = qn(field_name)
|
||||
|
||||
# See if a PostGIS Geometry function matches the lookup type next
|
||||
lookup_info = ORACLE_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)
|
||||
func, arg_type = lookup_info
|
||||
|
||||
# Ensuring that a tuple _value_ was passed in from the user
|
||||
if not isinstance(value, tuple) or len(value) != 2:
|
||||
raise TypeError('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 TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1])))
|
||||
|
||||
if func == 'dwithin':
|
||||
# TODO: test and consider adding different distance options.
|
||||
return "%s(%s, %%s, 'distance=%s')" % (func, table_prefix + field_name, value[1])
|
||||
else:
|
||||
return "%s(%s, %%s, %%s) = 'TRUE'" % (func, table_prefix + field_name)
|
||||
else:
|
||||
# Returning the SQL necessary for the geometry function call. For example:
|
||||
# SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE'
|
||||
return "%s(%s, %%s) = 'TRUE'" % (lookup_info, table_prefix + field_name)
|
||||
|
||||
# Handling 'isnull' lookup type
|
||||
if lookup_type == 'isnull':
|
||||
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
|
||||
|
||||
raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
|
||||
|
||||
ASGML = 'SDO_UTIL.TO_GMLGEOMETRY'
|
||||
UNION = 'SDO_AGGR_UNION'
|
||||
TRANSFORM = 'SDO_CS.TRANSFORM'
|
||||
|
||||
# Want to get SDO Geometries as WKT (much easier to instantiate GEOS proxies
|
||||
# from WKT than SDO_GEOMETRY(...) strings ;)
|
||||
GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'
|
@ -119,7 +119,7 @@ class PostGISField(Field):
|
||||
# Adding the necessary string substitutions and parameters
|
||||
# to perform a geometry transformation.
|
||||
return GeoFieldSQL(['%s(%%s,%%s)' % TRANSFORM],
|
||||
[adapt, self._srid])
|
||||
[adapt, self._srid])
|
||||
else:
|
||||
return GeoFieldSQL(['%s'], [adapt])
|
||||
else:
|
||||
|
@ -26,8 +26,8 @@ class GeometryColumns(models.Model):
|
||||
db_table = 'geometry_columns'
|
||||
|
||||
@classmethod
|
||||
def table_name(self):
|
||||
"Class method for returning the table name field for this model."
|
||||
def table_name_col(self):
|
||||
"Class method for returning the table name column for this model."
|
||||
return 'f_table_name'
|
||||
|
||||
def __unicode__(self):
|
||||
|
@ -105,8 +105,8 @@ MISC_TERMS = ['isnull']
|
||||
|
||||
# These are the PostGIS-customized QUERY_TERMS -- a list of the lookup types
|
||||
# allowed for geographic queries.
|
||||
POSTGIS_TERMS = list(POSTGIS_OPERATORS.keys()) # Getting the operators first
|
||||
POSTGIS_TERMS += list(POSTGIS_GEOMETRY_FUNCTIONS.keys()) # Adding on the Geometry Functions
|
||||
POSTGIS_TERMS = POSTGIS_OPERATORS.keys() # Getting the operators first
|
||||
POSTGIS_TERMS += POSTGIS_GEOMETRY_FUNCTIONS.keys() # Adding on the Geometry Functions
|
||||
POSTGIS_TERMS += MISC_TERMS # Adding any other miscellaneous terms (e.g., 'isnull')
|
||||
POSTGIS_TERMS = tuple(POSTGIS_TERMS) # Making immutable
|
||||
|
||||
|
@ -6,7 +6,7 @@ from django.db.models.fields import FieldDoesNotExist
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.contrib.gis.db.models.fields import GeometryField
|
||||
# parse_lookup depends on the spatial database backend.
|
||||
from django.contrib.gis.db.backend import parse_lookup, ASGML, ASKML, GEOM_SELECT, TRANSFORM, UNION
|
||||
from django.contrib.gis.db.backend import parse_lookup, ASGML, ASKML, GEOM_SELECT, SPATIAL_BACKEND, TRANSFORM, UNION
|
||||
from django.contrib.gis.geos import GEOSGeometry
|
||||
|
||||
class GeoQ(Q):
|
||||
@ -51,7 +51,7 @@ class GeoQuerySet(QuerySet):
|
||||
clone._filters = clone._filters & reduce(operator.and_, map(mapper, args))
|
||||
return clone
|
||||
|
||||
def _get_sql_clause(self):
|
||||
def _get_sql_clause(self, get_full_query=False):
|
||||
qn = connection.ops.quote_name
|
||||
opts = self.model._meta
|
||||
|
||||
@ -147,12 +147,61 @@ class GeoQuerySet(QuerySet):
|
||||
sql.append("ORDER BY " + ", ".join(order_by))
|
||||
|
||||
# LIMIT and OFFSET clauses
|
||||
if self._limit is not None:
|
||||
sql.append("%s " % connection.ops.limit_offset_sql(self._limit, self._offset))
|
||||
else:
|
||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||
if SPATIAL_BACKEND != 'oracle':
|
||||
if self._limit is not None:
|
||||
sql.append("%s " % connection.ops.limit_offset_sql(self._limit, self._offset))
|
||||
else:
|
||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||
|
||||
return select, " ".join(sql), params
|
||||
return select, " ".join(sql), params
|
||||
else:
|
||||
# To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
|
||||
select_clause = ",".join(select)
|
||||
distinct = (self._distinct and "DISTINCT " or "")
|
||||
|
||||
if order_by:
|
||||
order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
|
||||
else:
|
||||
#Oracle's row_number() function always requires an order-by clause.
|
||||
#So we need to define a default order-by, since none was provided.
|
||||
order_by_clause = " OVER (ORDER BY %s.%s)" % \
|
||||
(qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column))
|
||||
# limit_and_offset_clause
|
||||
if self._limit is None:
|
||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||
|
||||
if self._offset is not None:
|
||||
offset = int(self._offset)
|
||||
else:
|
||||
offset = 0
|
||||
if self._limit is not None:
|
||||
limit = int(self._limit)
|
||||
else:
|
||||
limit = None
|
||||
|
||||
limit_and_offset_clause = ''
|
||||
if limit is not None:
|
||||
limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
|
||||
elif offset:
|
||||
limit_and_offset_clause = "WHERE rn > %s" % (offset)
|
||||
|
||||
if len(limit_and_offset_clause) > 0:
|
||||
fmt = \
|
||||
"""SELECT * FROM
|
||||
(SELECT %s%s,
|
||||
ROW_NUMBER()%s AS rn
|
||||
%s)
|
||||
%s"""
|
||||
full_query = fmt % (distinct, select_clause,
|
||||
order_by_clause, ' '.join(sql).strip(),
|
||||
limit_and_offset_clause)
|
||||
else:
|
||||
full_query = None
|
||||
|
||||
if get_full_query:
|
||||
return select, " ".join(sql), params, full_query
|
||||
else:
|
||||
return select, " ".join(sql), params
|
||||
|
||||
def _clone(self, klass=None, **kwargs):
|
||||
c = super(GeoQuerySet, self)._clone(klass, **kwargs)
|
||||
@ -192,8 +241,13 @@ class GeoQuerySet(QuerySet):
|
||||
if not field_col:
|
||||
raise TypeError('GML output only available on GeometryFields')
|
||||
|
||||
# Adding AsGML function call to SELECT part of the SQL.
|
||||
return self.extra(select={'gml':'%s(%s,%s,%s)' % (ASGML, field_col, precision, version)})
|
||||
if SPATIAL_BACKEND == 'oracle':
|
||||
gml_select = {'gml':'%s(%s)' % (ASGML, field_col)}
|
||||
else:
|
||||
gml_select = {'gml':'%s(%s,%s,%s)' % (ASGML, field_col, precision, version)}
|
||||
|
||||
# Adding GML function call to SELECT part of the SQL.
|
||||
return self.extra(select=gml_select)
|
||||
|
||||
def kml(self, field_name, precision=8):
|
||||
"""
|
||||
@ -227,15 +281,19 @@ class GeoQuerySet(QuerySet):
|
||||
|
||||
# Setting the key for the field's column with the custom SELECT SQL to
|
||||
# override the geometry column returned from the database.
|
||||
self._custom_select[field.column] = \
|
||||
'(%s(%s, %s)) AS %s' % (TRANSFORM, col, srid,
|
||||
connection.ops.quote_name(field.column))
|
||||
if SPATIAL_BACKEND == 'oracle':
|
||||
custom_sel = '%s(%s, %s)' % (TRANSFORM, col, srid)
|
||||
else:
|
||||
custom_sel = '(%s(%s, %s)) AS %s' % \
|
||||
(TRANSFORM, col, srid, connection.ops.quote_name(field.column))
|
||||
self._custom_select[field.column] = custom_sel
|
||||
return self._clone()
|
||||
|
||||
def union(self, field_name):
|
||||
def union(self, field_name, tolerance=0.0005):
|
||||
"""
|
||||
Performs an aggregate union on the given geometry field. Returns
|
||||
None if the GeoQuerySet is empty.
|
||||
None if the GeoQuerySet is empty. The `tolerance` keyword is for
|
||||
Oracle backends only.
|
||||
"""
|
||||
# Making sure backend supports the Union stored procedure
|
||||
if not UNION:
|
||||
@ -254,11 +312,24 @@ class GeoQuerySet(QuerySet):
|
||||
|
||||
# Replacing the select with a call to the ST_Union stored procedure
|
||||
# on the geographic field column.
|
||||
union_sql = ('SELECT %s(%s)' % (UNION, field_col)) + sql
|
||||
if SPATIAL_BACKEND == 'oracle':
|
||||
union_sql = 'SELECT %s' % self._geo_fmt
|
||||
union_sql = union_sql % ('%s(SDOAGGRTYPE(%s,%s))' % (UNION, field_col, tolerance))
|
||||
union_sql += sql
|
||||
else:
|
||||
union_sql = ('SELECT %s(%s)' % (UNION, field_col)) + sql
|
||||
|
||||
# Getting a cursor, executing the query.
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(union_sql, params)
|
||||
|
||||
# Pulling the HEXEWKB from the returned cursor.
|
||||
hex = cursor.fetchone()[0]
|
||||
if hex: return GEOSGeometry(hex)
|
||||
if SPATIAL_BACKEND == 'oracle':
|
||||
# On Oracle have to read out WKT from CLOB first.
|
||||
clob = cursor.fetchone()[0]
|
||||
if clob: u = clob.read()
|
||||
else: u = None
|
||||
else:
|
||||
u = cursor.fetchone()[0]
|
||||
|
||||
if u: return GEOSGeometry(u)
|
||||
else: return None
|
||||
|
@ -29,12 +29,25 @@
|
||||
http://zcologia.com/news/429/geometries-for-python-update/
|
||||
"""
|
||||
|
||||
from django.contrib.gis.geos.base import GEOSGeometry
|
||||
from django.contrib.gis.geos.base import GEOSGeometry, wkt_regex, hex_regex
|
||||
from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon, HAS_NUMPY
|
||||
from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
|
||||
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||
from django.contrib.gis.geos.libgeos import geos_version
|
||||
|
||||
def fromfile(file_name):
|
||||
"""
|
||||
Given a string file name, returns a GEOSGeometry. The file may contain WKB,
|
||||
WKT, or HEX.
|
||||
"""
|
||||
fh = open(file_name, 'rb')
|
||||
buf = fh.read()
|
||||
fh.close()
|
||||
if wkt_regex.match(buf) or hex_regex.match(buf):
|
||||
return GEOSGeometry(buf)
|
||||
else:
|
||||
return GEOSGeometry(buffer(buf))
|
||||
|
||||
def fromstr(wkt_or_hex, **kwargs):
|
||||
"Given a string value (wkt or hex), returns a GEOSGeometry object."
|
||||
return GEOSGeometry(wkt_or_hex, **kwargs)
|
||||
|
@ -120,5 +120,7 @@ class SpatialRefSysMixin(object):
|
||||
# The SpatialRefSys and GeometryColumns models
|
||||
if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
|
||||
from django.contrib.gis.db.backend.postgis.models import GeometryColumns, SpatialRefSys
|
||||
elif settings.DATABASE_ENGINE == 'oracle':
|
||||
from django.contrib.gis.db.backend.oracle.models import GeometryColumns, SpatialRefSys
|
||||
else:
|
||||
raise NotImplementedError('No SpatialRefSys or GeometryColumns models for backend: %s' % settings.DATABASE_ENGINE)
|
||||
|
1
django/contrib/gis/tests/geoapp/sql/co.wkt
Normal file
1
django/contrib/gis/tests/geoapp/sql/co.wkt
Normal file
File diff suppressed because one or more lines are too long
1
django/contrib/gis/tests/geoapp/sql/ks.wkt
Normal file
1
django/contrib/gis/tests/geoapp/sql/ks.wkt
Normal file
File diff suppressed because one or more lines are too long
1
django/contrib/gis/tests/geoapp/sql/nz.wkt
Normal file
1
django/contrib/gis/tests/geoapp/sql/nz.wkt
Normal file
File diff suppressed because one or more lines are too long
1
django/contrib/gis/tests/geoapp/sql/tx.wkt
Normal file
1
django/contrib/gis/tests/geoapp/sql/tx.wkt
Normal file
File diff suppressed because one or more lines are too long
@ -1,12 +1,33 @@
|
||||
import unittest
|
||||
import os, unittest
|
||||
from models import Country, City, State, Feature
|
||||
from django.contrib.gis.geos import *
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geos import *
|
||||
from django.contrib.gis.tests.utils import no_oracle, no_postgis, oracle, postgis
|
||||
|
||||
class GeoModelTest(unittest.TestCase):
|
||||
|
||||
def test01_initial_sql(self):
|
||||
"Testing geographic initial SQL."
|
||||
if oracle:
|
||||
# Oracle doesn't allow strings longer than 4000 characters
|
||||
# in SQL files, and I'm stumped on how to use Oracle BFILE's
|
||||
# in PLSQL, so we set up the larger geometries manually, rather
|
||||
# than relying on the initial SQL.
|
||||
|
||||
# Routine for returning the path to the data files.
|
||||
data_dir = os.path.join(os.path.dirname(__file__), 'sql')
|
||||
def get_file(wkt_file):
|
||||
return os.path.join(data_dir, wkt_file)
|
||||
|
||||
co = State(name='Colorado', poly=fromfile(get_file('co.wkt')))
|
||||
co.save()
|
||||
ks = State(name='Kansas', poly=fromfile(get_file('ks.wkt')))
|
||||
ks.save()
|
||||
tx = Country(name='Texas', mpoly=fromfile(get_file('tx.wkt')))
|
||||
tx.save()
|
||||
nz = Country(name='New Zealand', mpoly=fromfile(get_file('nz.wkt')))
|
||||
nz.save()
|
||||
|
||||
# Ensuring that data was loaded from initial SQL.
|
||||
self.assertEqual(2, Country.objects.count())
|
||||
self.assertEqual(8, City.objects.count())
|
||||
@ -80,6 +101,7 @@ class GeoModelTest(unittest.TestCase):
|
||||
self.assertEqual(ply, State.objects.get(name='NullState').poly)
|
||||
nullstate.delete()
|
||||
|
||||
@no_oracle # Oracle does not support KML.
|
||||
def test03a_kml(self):
|
||||
"Testing KML output from the database using GeoManager.kml()."
|
||||
# Should throw a TypeError when trying to obtain KML from a
|
||||
@ -98,7 +120,13 @@ class GeoModelTest(unittest.TestCase):
|
||||
qs = City.objects.all()
|
||||
self.assertRaises(TypeError, qs.gml, 'name')
|
||||
ptown = City.objects.gml('point', precision=9).get(name='Pueblo')
|
||||
self.assertEqual('<gml:Point srsName="EPSG:4326"><gml:coordinates>-104.609252,38.255001</gml:coordinates></gml:Point>', ptown.gml)
|
||||
if oracle:
|
||||
# No precision parameter for Oracle :-/
|
||||
import re
|
||||
gml_regex = re.compile(r'<gml:Point srsName="SDO:4326" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="\." cs="," ts=" ">-104.60925199\d+,38.25500\d+ </gml:coordinates></gml:Point>')
|
||||
self.assertEqual(True, bool(gml_regex.match(ptown.gml)))
|
||||
else:
|
||||
self.assertEqual('<gml:Point srsName="EPSG:4326"><gml:coordinates>-104.609252,38.255001</gml:coordinates></gml:Point>', ptown.gml)
|
||||
|
||||
def test04_transform(self):
|
||||
"Testing the transform() GeoManager method."
|
||||
@ -107,14 +135,15 @@ class GeoModelTest(unittest.TestCase):
|
||||
ptown = fromstr('POINT(992363.390841912 481455.395105533)', srid=2774)
|
||||
|
||||
# Asserting the result of the transform operation with the values in
|
||||
# the pre-transformed points.
|
||||
h = City.objects.transform('point', srid=htown.srid).get(name='Houston')
|
||||
self.assertAlmostEqual(htown.x, h.point.x, 8)
|
||||
self.assertAlmostEqual(htown.y, h.point.y, 8)
|
||||
# the pre-transformed points. Oracle does not have the 3084 SRID.
|
||||
if not oracle:
|
||||
h = City.objects.transform('point', srid=htown.srid).get(name='Houston')
|
||||
self.assertAlmostEqual(htown.x, h.point.x, 8)
|
||||
self.assertAlmostEqual(htown.y, h.point.y, 8)
|
||||
|
||||
p = City.objects.transform('point', srid=ptown.srid).get(name='Pueblo')
|
||||
self.assertAlmostEqual(ptown.x, p.point.x, 8)
|
||||
self.assertAlmostEqual(ptown.y, p.point.y, 8)
|
||||
self.assertAlmostEqual(ptown.x, p.point.x, 7)
|
||||
self.assertAlmostEqual(ptown.y, p.point.y, 7)
|
||||
|
||||
def test10_contains_contained(self):
|
||||
"Testing the 'contained', 'contains', and 'bbcontains' lookup types."
|
||||
@ -124,10 +153,11 @@ class GeoModelTest(unittest.TestCase):
|
||||
# Seeing what cities are in Texas, should get Houston and Dallas,
|
||||
# and Oklahoma City because 'contained' only checks on the
|
||||
# _bounding box_ of the Geometries.
|
||||
qs = City.objects.filter(point__contained=texas.mpoly)
|
||||
self.assertEqual(3, qs.count())
|
||||
cities = ['Houston', 'Dallas', 'Oklahoma City']
|
||||
for c in qs: self.assertEqual(True, c.name in cities)
|
||||
if not oracle:
|
||||
qs = City.objects.filter(point__contained=texas.mpoly)
|
||||
self.assertEqual(3, qs.count())
|
||||
cities = ['Houston', 'Dallas', 'Oklahoma City']
|
||||
for c in qs: self.assertEqual(True, c.name in cities)
|
||||
|
||||
# Pulling out some cities.
|
||||
houston = City.objects.get(name='Houston')
|
||||
@ -151,20 +181,37 @@ class GeoModelTest(unittest.TestCase):
|
||||
self.assertEqual(0, len(Country.objects.filter(mpoly__contains=okcity.point.wkt))) # Qeury w/WKT
|
||||
|
||||
# OK City is contained w/in bounding box of Texas.
|
||||
qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
|
||||
self.assertEqual(1, len(qs))
|
||||
self.assertEqual('Texas', qs[0].name)
|
||||
if not oracle:
|
||||
qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
|
||||
self.assertEqual(1, len(qs))
|
||||
self.assertEqual('Texas', qs[0].name)
|
||||
|
||||
def test11_lookup_insert_transform(self):
|
||||
"Testing automatic transform for lookups and inserts."
|
||||
# San Antonio in 'WGS84' (SRID 4326) and 'NAD83(HARN) / Texas Centric Lambert Conformal' (SRID 3084)
|
||||
# San Antonio in 'WGS84' (SRID 4326)
|
||||
sa_4326 = 'POINT (-98.493183 29.424170)'
|
||||
sa_3084 = 'POINT (1645978.362408288754523 6276356.025927528738976)' # Used ogr.py in gdal 1.4.1 for this transform
|
||||
|
||||
# Constructing & querying with a point from a different SRID
|
||||
wgs_pnt = fromstr(sa_4326, srid=4326) # Our reference point in WGS84
|
||||
nad_pnt = fromstr(sa_3084, srid=3084)
|
||||
tx = Country.objects.get(mpoly__intersects=nad_pnt)
|
||||
|
||||
# Oracle doesn't have SRID 3084, using 41157.
|
||||
if oracle:
|
||||
# San Antonio in 'Texas 4205, Southern Zone (1983, meters)' (SRID 41157)
|
||||
# Used the following Oracle SQL to get this value:
|
||||
# SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM(SDO_GEOMETRY('POINT (-98.493183 29.424170)', 4326), 41157)) FROM DUAL;
|
||||
nad_wkt = 'POINT (300662.034646583 5416427.45974934)'
|
||||
nad_srid = 41157
|
||||
else:
|
||||
# San Antonio in 'NAD83(HARN) / Texas Centric Lambert Conformal' (SRID 3084)
|
||||
nad_wkt = 'POINT (1645978.362408288754523 6276356.025927528738976)' # Used ogr.py in gdal 1.4.1 for this transform
|
||||
nad_srid = 3084
|
||||
|
||||
# Constructing & querying with a point from a different SRID. Oracle
|
||||
# `SDO_OVERLAPBDYINTERSECT` operates differently from
|
||||
# `ST_Intersects`, so contains is used instead.
|
||||
nad_pnt = fromstr(nad_wkt, srid=nad_srid)
|
||||
if oracle:
|
||||
tx = Country.objects.get(mpoly__contains=nad_pnt)
|
||||
else:
|
||||
tx = Country.objects.get(mpoly__intersects=nad_pnt)
|
||||
self.assertEqual('Texas', tx.name)
|
||||
|
||||
# Creating San Antonio. Remember the Alamo.
|
||||
@ -177,7 +224,7 @@ class GeoModelTest(unittest.TestCase):
|
||||
self.assertAlmostEqual(wgs_pnt.y, sa.point.y, 6)
|
||||
|
||||
def test12_null_geometries(self):
|
||||
"Testing NULL geometry support."
|
||||
"Testing NULL geometry support, and the `isnull` lookup type."
|
||||
# Querying for both NULL and Non-NULL values.
|
||||
nullqs = State.objects.filter(poly__isnull=True)
|
||||
validqs = State.objects.filter(poly__isnull=False)
|
||||
@ -193,9 +240,12 @@ class GeoModelTest(unittest.TestCase):
|
||||
self.assertEqual(True, 'Kansas' in state_names)
|
||||
|
||||
# Saving another commonwealth w/a NULL geometry.
|
||||
nmi = State(name='Northern Mariana Islands', poly=None)
|
||||
nmi.save()
|
||||
if not oracle:
|
||||
# TODO: Fix saving w/NULL geometry on Oracle.
|
||||
nmi = State(name='Northern Mariana Islands', poly=None)
|
||||
nmi.save()
|
||||
|
||||
@no_oracle # No specific `left` or `right` operators in Oracle.
|
||||
def test13_left_right(self):
|
||||
"Testing the 'left' and 'right' lookup types."
|
||||
# Left: A << B => true if xmax(A) < xmin(B)
|
||||
@ -240,6 +290,7 @@ class GeoModelTest(unittest.TestCase):
|
||||
c3 = City.objects.get(point__equals=pnt)
|
||||
for c in [c1, c2, c3]: self.assertEqual('Houston', c.name)
|
||||
|
||||
@no_oracle # Oracle SDO_RELATE() uses a different system.
|
||||
def test15_relate(self):
|
||||
"Testing the 'relate' lookup type."
|
||||
# To make things more interesting, we will have our Texas reference point in
|
||||
|
@ -1,8 +1,9 @@
|
||||
import unittest
|
||||
from django.contrib.gis.models import SpatialRefSys
|
||||
from django.contrib.gis.tests.utils import oracle, postgis
|
||||
|
||||
test_srs = ({'srid' : 4326,
|
||||
'auth_name' : 'EPSG',
|
||||
'auth_name' : ('EPSG', True),
|
||||
'auth_srid' : 4326,
|
||||
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
|
||||
'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
|
||||
@ -12,7 +13,7 @@ test_srs = ({'srid' : 4326,
|
||||
'eprec' : (1, 1, 9),
|
||||
},
|
||||
{'srid' : 32140,
|
||||
'auth_name' : 'EPSG',
|
||||
'auth_name' : ('EPSG', False),
|
||||
'auth_srid' : 32140,
|
||||
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
|
||||
'proj4' : '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
|
||||
@ -30,24 +31,35 @@ class SpatialRefSysTest(unittest.TestCase):
|
||||
for sd in test_srs:
|
||||
srs = SpatialRefSys.objects.get(srid=sd['srid'])
|
||||
self.assertEqual(sd['srid'], srs.srid)
|
||||
self.assertEqual(sd['auth_name'], srs.auth_name)
|
||||
|
||||
# Some of the authority names are borked on Oracle, e.g., SRID=32140.
|
||||
# also, Oracle Spatial seems to add extraneous info to fields, hence the
|
||||
# the testing with the 'startswith' flag.
|
||||
auth_name, oracle_flag = sd['auth_name']
|
||||
if postgis or (oracle and oracle_flag):
|
||||
self.assertEqual(True, srs.auth_name.startswith(auth_name))
|
||||
|
||||
self.assertEqual(sd['auth_srid'], srs.auth_srid)
|
||||
self.assertEqual(sd['srtext'], srs.srtext)
|
||||
self.assertEqual(sd['proj4'], srs.proj4text)
|
||||
|
||||
# No proj.4 and different srtext on oracle backends :(
|
||||
if postgis:
|
||||
self.assertEqual(sd['srtext'], srs.wkt)
|
||||
self.assertEqual(sd['proj4'], srs.proj4text)
|
||||
|
||||
def test02_osr(self):
|
||||
"Testing getting OSR objects from SpatialRefSys model objects."
|
||||
for sd in test_srs:
|
||||
sr = SpatialRefSys.objects.get(srid=sd['srid'])
|
||||
self.assertEqual(sd['spheroid'], sr.spheroid)
|
||||
self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
|
||||
self.assertEqual(sd['geographic'], sr.geographic)
|
||||
self.assertEqual(sd['projected'], sr.projected)
|
||||
self.assertEqual(sd['name'], sr.name)
|
||||
self.assertEqual(True, sr.name.startswith(sd['name']))
|
||||
|
||||
# Testing the SpatialReference object directly.
|
||||
srs = sr.srs
|
||||
self.assertEqual(sd['proj4'], srs.proj4)
|
||||
self.assertEqual(sd['srtext'], srs.wkt)
|
||||
if postgis:
|
||||
srs = sr.srs
|
||||
self.assertEqual(sd['proj4'], srs.proj4)
|
||||
self.assertEqual(sd['srtext'], srs.wkt)
|
||||
|
||||
def test03_ellipsoid(self):
|
||||
"Testing the ellipsoid property."
|
||||
|
20
django/contrib/gis/tests/utils.py
Normal file
20
django/contrib/gis/tests/utils.py
Normal file
@ -0,0 +1,20 @@
|
||||
from django.conf import settings
|
||||
|
||||
# function that will pass a test.
|
||||
def pass_test(*args): return
|
||||
|
||||
def no_backend(test_func, backend):
|
||||
"Use this decorator to disable test on specified backend."
|
||||
if settings.DATABASE_ENGINE == backend:
|
||||
return pass_test
|
||||
else:
|
||||
return test_func
|
||||
|
||||
# Decorators to disable entire test functions for specific
|
||||
# spatial backends.
|
||||
def no_oracle(func): return no_backend(func, 'oracle')
|
||||
def no_postgis(func): return no_backend(func, 'postgresql_psycopg2')
|
||||
|
||||
# Shortcut booleans to omit only portions of tests.
|
||||
oracle = settings.DATABASE_ENGINE == 'oracle'
|
||||
postgis = settings.DATABASE_ENGINE == 'postgresql_psycopg2'
|
@ -15,3 +15,4 @@ try:
|
||||
HAS_GEOIP = True
|
||||
except:
|
||||
HAS_GEOIP = False
|
||||
|
||||
|
@ -99,6 +99,7 @@ Example:
|
||||
"""
|
||||
from types import StringType, TupleType
|
||||
from datetime import datetime
|
||||
from django.contrib.gis.db.backend import SPATIAL_BACKEND
|
||||
from django.contrib.gis.gdal import \
|
||||
OGRGeometry, OGRGeomType, SpatialReference, CoordTransform, \
|
||||
DataSource, OGRException
|
||||
@ -176,19 +177,19 @@ def check_feature(feat, model_fields, mapping):
|
||||
elif model_field[:-3] in model_fields: #foreign key
|
||||
model_type = model_fields[model_field[:-3]]
|
||||
else:
|
||||
raise Exception, 'Given mapping field "%s" not in given Model fields!' % model_field
|
||||
raise Exception('Given mapping field "%s" not in given Model fields!' % model_field)
|
||||
|
||||
### Handling if we get a geometry in the Field ###
|
||||
if ogr_field in ogc_types:
|
||||
# At this time, no more than one geographic field per model =(
|
||||
if HAS_GEO:
|
||||
raise Exception, 'More than one geographic field in mapping not allowed (yet).'
|
||||
raise Exception('More than one geographic field in mapping not allowed (yet).')
|
||||
else:
|
||||
HAS_GEO = ogr_field
|
||||
|
||||
# Making sure this geometry field type is a valid Django GIS field.
|
||||
if not model_type in gis_fields:
|
||||
raise Exception, 'Unknown Django GIS field type "%s"' % model_type
|
||||
raise Exception('Unknown Django GIS field type "%s"' % model_type)
|
||||
|
||||
# Getting the OGRGeometry, it's type (an integer) and it's name (a string)
|
||||
geom = feat.geom
|
||||
@ -202,20 +203,20 @@ def check_feature(feat, model_fields, mapping):
|
||||
# The geometry type otherwise was expected
|
||||
pass
|
||||
else:
|
||||
raise Exception, 'Invalid mapping geometry; model has %s, feature has %s' % (model_type, gtype)
|
||||
raise Exception('Invalid mapping geometry; model has %s, feature has %s' % (model_type, gtype))
|
||||
|
||||
## Handling other fields
|
||||
else:
|
||||
# Making sure the model field is
|
||||
if not model_type in field_types:
|
||||
raise Exception, 'Django field type "%s" has no OGR mapping (yet).' % model_type
|
||||
raise Exception('Django field type "%s" has no OGR mapping (yet).' % model_type)
|
||||
|
||||
# Otherwise, we've got an OGR Field. Making sure that an
|
||||
# index exists for the mapping OGR field.
|
||||
try:
|
||||
fi = feat.index(ogr_field)
|
||||
except:
|
||||
raise Exception, 'Given mapping OGR field "%s" not in given OGR layer feature!' % ogr_field
|
||||
raise Exception('Given mapping OGR field "%s" not in given OGR layer feature!' % ogr_field)
|
||||
|
||||
def check_layer(layer, fields, mapping):
|
||||
"Checks the OGR layer by incrementing through and checking each feature."
|
||||
@ -234,7 +235,7 @@ def check_srs(layer, source_srs):
|
||||
else:
|
||||
sr = layer.srs
|
||||
if not sr:
|
||||
raise Exception, 'No source reference system defined.'
|
||||
raise Exception('No source reference system defined.')
|
||||
else:
|
||||
return sr
|
||||
|
||||
@ -280,9 +281,12 @@ class LayerMapping:
|
||||
|
||||
# Getting the GeometryColumn object.
|
||||
try:
|
||||
geo_col = GeometryColumns.objects.get(f_table_name=self.model._meta.db_table)
|
||||
db_table = self.model._meta.db_table
|
||||
if SPATIAL_BACKEND == 'oracle': db_table = db_table.upper()
|
||||
gc_kwargs = {GeometryColumns.table_name_col() : db_table}
|
||||
geo_col = GeometryColumns.objects.get(**gc_kwargs)
|
||||
except:
|
||||
raise Exception, 'Geometry column does not exist. (did you run syncdb?)'
|
||||
raise Exception('Geometry column does not exist. (did you run syncdb?)')
|
||||
|
||||
# Getting the coordinate system needed for transformation (with CoordTransform)
|
||||
try:
|
||||
@ -292,7 +296,7 @@ class LayerMapping:
|
||||
# Creating the CoordTransform object
|
||||
ct = CoordTransform(self.source_srs, target_srs)
|
||||
except Exception, msg:
|
||||
raise Exception, 'Could not translate between the data source and model geometry: %s' % msg
|
||||
raise Exception('Could not translate between the data source and model geometry: %s' % msg)
|
||||
|
||||
for feat in self.layer:
|
||||
# The keyword arguments for model construction
|
||||
|
@ -445,14 +445,24 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
charset = 'utf-8'
|
||||
|
||||
def _format_params(self, params):
|
||||
sz_kwargs = {}
|
||||
if isinstance(params, dict):
|
||||
result = {}
|
||||
charset = self.charset
|
||||
for key, value in params.items():
|
||||
result[smart_str(key, charset)] = smart_str(value, charset)
|
||||
return result
|
||||
if hasattr(value, 'oracle_type'): sz_kwargs[key] = value.oracle_type()
|
||||
else:
|
||||
return tuple([smart_str(p, self.charset, True) for p in params])
|
||||
result = {}
|
||||
for i in xrange(len(params)):
|
||||
key = 'arg%d' % i
|
||||
result[key] = smart_str(params[i], self.charset, True)
|
||||
if hasattr(params[i], 'oracle_type'): sz_kwargs[key] = params[i].oracle_type()
|
||||
|
||||
# If any of the parameters had an `oracle_type` method, then we set
|
||||
# the inputsizes for those parameters using the returned type
|
||||
if sz_kwargs: self.setinputsizes(**sz_kwargs)
|
||||
return result
|
||||
|
||||
def execute(self, query, params=None):
|
||||
if params is None:
|
||||
@ -471,7 +481,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
|
||||
def executemany(self, query, params=None):
|
||||
try:
|
||||
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
||||
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
||||
except (IndexError, TypeError):
|
||||
# No params given, nothing to do
|
||||
return None
|
||||
|
@ -228,10 +228,11 @@ class Model(object):
|
||||
# If it does already exist, do an UPDATE.
|
||||
if cursor.fetchone():
|
||||
db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
|
||||
placeholders = [f.get_placeholder(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
|
||||
if db_values:
|
||||
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
||||
(qn(self._meta.db_table),
|
||||
','.join(['%s=%%s' % qn(f.column) for f in non_pks]),
|
||||
','.join(['%s=%s' % (qn(f.column), placeholders[i]) for i, f in enumerate(non_pks)]),
|
||||
qn(self._meta.pk.column)),
|
||||
db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
||||
else:
|
||||
|
Loading…
x
Reference in New Issue
Block a user