1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

gis: (1) fixed WKT admin bug

(2) cleaned up GeometryField (moved SQL creation into field, index creation now on by default) 
     (3) now have exact lookup type, e.g., filter(geom='...') works


git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5336 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-05-25 01:29:10 +00:00
parent 58aa35e7a5
commit f84bb1b18f
3 changed files with 75 additions and 63 deletions

View File

@ -1,60 +1,78 @@
# The Django base Field class. # The Django base Field class.
from django.db.models.fields import Field from django.db.models.fields import Field
from django.contrib.gis.db.models.postgis import POSTGIS_TERMS from django.contrib.gis.db.models.postgis import POSTGIS_TERMS, quotename
from django.contrib.gis.oldforms import WKTField from django.contrib.gis.oldforms import WKTField
from django.utils.functional import curry from django.utils.functional import curry
from django.contrib.gis.geos import GEOSGeometry, GEOSException
#TODO: add db.quotename.
# Creates the SQL to add the model to the database.
def _add_geom(geom, srid, style, model, field, dim=2):
# Constructing the AddGeometryColumn(...) command -- the style
# object is passed in from the management module and is used
# to syntax highlight the command for 'sqlall'
sql = style.SQL_KEYWORD('SELECT ') + \
style.SQL_TABLE('AddGeometryColumn') + "('" + \
style.SQL_TABLE(model) + "', '" + \
style.SQL_FIELD(field) + "', " + \
style.SQL_FIELD(str(srid)) + ", '" + \
style.SQL_COLTYPE(geom) + "', " + \
style.SQL_KEYWORD(str(dim)) + \
');'
return sql
# Creates an index for the given geometry.
def _geom_index(geom, style, model, field,
index_type='GIST',
index_opts='GIST_GEOMETRY_OPS'):
sql = style.SQL_KEYWORD('CREATE INDEX ') + \
style.SQL_TABLE('"%s_%s_id"' % (model, field)) + \
style.SQL_KEYWORD(' ON ') + \
style.SQL_TABLE('"%s"' % model) + \
style.SQL_KEYWORD(' USING ') + \
style.SQL_COLTYPE(index_type) + ' ( ' + \
style.SQL_FIELD('"%s"' % field) + ' ' + \
style.SQL_KEYWORD(index_opts) + ' );'
return sql
class GeometryField(Field): class GeometryField(Field):
"The base GIS field -- maps to the OpenGIS Geometry type." "The base GIS field -- maps to the OpenGIS Specification Geometry type."
# The OpenGIS Geometry name. # The OpenGIS Geometry name.
_geom = 'GEOMETRY' _geom = 'GEOMETRY'
def __init__(self, srid=4326, index=False, **kwargs): def __init__(self, srid=4326, index=True, dim=2, **kwargs):
#TODO: SRID a standard, or specific to postgis? """The initialization function for geometry fields. Takes the following
# Whether or not index this field, defaults to False as keyword arguments:
# Why can't we just use db_index?
# TODO: Move index creation (and kwarg lookup, and...) srid - The spatial reference system identifier. An OGC standard.
# into Field rather than core.management and db.models.query. Defaults to 4326 (WGS84)
index - Indicates whether to create a GiST index. Defaults to True.
Set this instead of 'db_index' for geographic fields since index
creation is different for geometry columns.
dim - The number of dimensions for this geometry. Defaults to 2.
"""
self._index = index self._index = index
# The SRID for the geometry, defaults to 4326.
self._srid = srid self._srid = srid
self._dim = dim
super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function
# Calling the Field initialization function first def _add_geom(self, style, db_table, field):
super(GeometryField, self).__init__(**kwargs) """Constructs the addition of the geometry to the table using the
AddGeometryColumn(...) PostGIS (and OGC standard) function.
Takes the style object (provides syntax highlighting) as well as the
database table and field. The dimensions can be specified via
the dim keyword as well.
"""
sql = style.SQL_KEYWORD('SELECT ') + \
style.SQL_TABLE('AddGeometryColumn') + '(' + \
style.SQL_TABLE(quotename(db_table)) + ', ' + \
style.SQL_FIELD(quotename(field)) + ', ' + \
style.SQL_FIELD(str(self._srid)) + ', ' + \
style.SQL_COLTYPE(quotename(self._geom)) + ', ' + \
style.SQL_KEYWORD(str(self._dim)) + ');'
return sql
def _geom_index(self, style, db_table, field,
index_type='GIST', index_opts='GIST_GEOMETRY_OPS'):
"Creates a GiST index for this geometry field."
sql = style.SQL_KEYWORD('CREATE INDEX ') + \
style.SQL_TABLE(quotename('%s_%s_id' % (db_table, field), dbl=True)) + \
style.SQL_KEYWORD(' ON ') + \
style.SQL_TABLE(quotename(db_table, dbl=True)) + \
style.SQL_KEYWORD(' USING ') + \
style.SQL_COLTYPE(index_type) + ' ( ' + \
style.SQL_FIELD(quotename(field, dbl=True)) + ' ' + \
style.SQL_KEYWORD(index_opts) + ' );'
return sql
def _post_create_sql(self, style, db_table, field):
"""Returns SQL that will be executed after the model has been
created. Geometry columns must be added after creation with the
PostGIS AddGeometryColumn() function."""
# Getting the AddGeometryColumn() SQL necessary to create a PostGIS
# geometry field.
post_sql = self._add_geom(style, db_table, field)
# If the user wants to index this data, then get the indexing SQL as well.
if self._index:
return '%s\n%s' % (post_sql, self._geom_index(style, db_table, field))
else:
return post_sql
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
super(GeometryField, self).contribute_to_class(cls, name) super(GeometryField, self).contribute_to_class(cls, name)
@ -68,22 +86,6 @@ class GeometryField(Field):
def get_internal_type(self): def get_internal_type(self):
return "NoField" return "NoField"
def _post_create_sql(self, *args, **kwargs):
"""Returns SQL that will be executed after the model has been created. Geometry
columns must be added after creation with the PostGIS AddGeometryColumn() function."""
#TODO: clean up *args/**kwargs.
# Getting the AddGeometryColumn() SQL necessary to create a PostGIS
# geometry field.
post_sql = _add_geom(self._geom, self._srid, *args, **kwargs)
# If the user wants to index this data, then get the indexing SQL as well.
if self._index:
return '%s\n%s' % (post_sql, _geom_index(self._geom, *args, **kwargs))
else:
return post_sql
def get_db_prep_lookup(self, lookup_type, value): def get_db_prep_lookup(self, lookup_type, value):
"""Returns field's value prepared for database lookup; the SRID of the geometry is """Returns field's value prepared for database lookup; the SRID of the geometry is
included by default in these queries.""" included by default in these queries."""

View File

@ -27,6 +27,7 @@ POSTGIS_OPERATORS = {
# The "~=" operator is the "same as" operator. It tests actual geometric equality of two features. So if # 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. # A and B are the same feature, vertex-by-vertex, the operator returns true.
'same_as' : '~= %s', 'same_as' : '~= %s',
'exact' : '~= %s',
# The "@" operator returns true if A's bounding box is completely contained by B's bounding box. # The "@" operator returns true if A's bounding box is completely contained by B's bounding box.
'contained' : '@ %s', 'contained' : '@ %s',
# The "~" operator returns true if A's bounding box completely contains B's bounding box. # The "~" operator returns true if A's bounding box completely contains B's bounding box.
@ -50,6 +51,11 @@ POSTGIS_GEOMETRY_FUNCTIONS = {
'relate' : 'Relate', 'relate' : 'Relate',
} }
# The quotation used for postgis (uses single quotes).
def quotename(value, dbl=False):
if dbl: return '"%s"' % value
else: return "'%s'" % value
# These are the PostGIS-customized QUERY_TERMS, combines both the operators # These are the PostGIS-customized QUERY_TERMS, combines both the operators
# and the geometry functions. # and the geometry functions.
POSTGIS_TERMS = list(POSTGIS_OPERATORS.keys()) # Getting the operators first POSTGIS_TERMS = list(POSTGIS_OPERATORS.keys()) # Getting the operators first

View File

@ -6,7 +6,11 @@ class WKTField(LargeTextField):
def render(self, data): def render(self, data):
# PostGIS uses EWKBHEX to store its values internally, converting # PostGIS uses EWKBHEX to store its values internally, converting
# to WKT for the admin first. # to WKT for the admin first -- unless there's no data, then just
return super(WKTField, self).render(hex_to_wkt(data)) # pass None to LargeTextField's render.
if not data:
return super(WKTField, self).render(None)
else:
return super(WKTField, self).render(hex_to_wkt(data))