diff --git a/django/contrib/gis/db/models/__init__.py b/django/contrib/gis/db/models/__init__.py index cffa6e9b61..5117ac60f6 100644 --- a/django/contrib/gis/db/models/__init__.py +++ b/django/contrib/gis/db/models/__init__.py @@ -6,7 +6,7 @@ from django.contrib.gis.db.models.manager import GeoManager # The various PostGIS/OpenGIS enabled fields. from django.contrib.gis.db.models.fields import \ - GeometryField, PointField, LineString, PolygonField, \ + GeometryField, PointField, LineStringField, PolygonField, \ MultiPointField, MultiLineStringField, MultiPolygonField, \ GeometryCollectionField diff --git a/django/contrib/gis/db/models/fields/__init__.py b/django/contrib/gis/db/models/fields/__init__.py index e2aedbcdb3..50c8bb6ef0 100644 --- a/django/contrib/gis/db/models/fields/__init__.py +++ b/django/contrib/gis/db/models/fields/__init__.py @@ -1,6 +1,10 @@ # The Django base Field class. from django.db.models.fields import Field +from django.oldforms import LargeTextField from django.contrib.gis.db.models.postgis import POSTGIS_TERMS +from geos import geomToWKT, geomFromHEX + +#TODO: add db.quotename. # Creates the SQL to add the model to the database. def _add_geom(geom, srid, style, model, field, dim=2): @@ -20,17 +24,26 @@ def _add_geom(geom, srid, style, model, field, dim=2): # Creates an index for the given geometry. def _geom_index(geom, style, model, field, index_type='GIST', - index_opts='GIST_GEOMETRY_OPTS'): + index_opts='GIST_GEOMETRY_OPS'): sql = style.SQL_KEYWORD('CREATE INDEX ') + \ - style.SQL_FIELD(field + '_idx') + \ + style.SQL_TABLE('"%s_%s_id"' % (model, field)) + \ style.SQL_KEYWORD(' ON ') + \ - style.SQL_TABLE(model) + \ + style.SQL_TABLE('"%s"' % model) + \ style.SQL_KEYWORD(' USING ') + \ style.SQL_COLTYPE(index_type) + ' ( ' + \ - style.SQL_FIELD(field) + ' ' + \ + style.SQL_FIELD('"%s"' % field) + ' ' + \ style.SQL_KEYWORD(index_opts) + ' );' return sql +class WKTField(LargeTextField): + "An oldforms LargeTextField for editing WKT text in the admin." + + def render(self, data): + # PostGIS uses EWKBHEX to store its values internally, converting + # to WKT for the admin first. + wkt = geomToWKT(geomFromHEX(data)) + return super(WKTField, self).render(wkt) + class GeometryField(Field): "The base GIS field -- maps to the OpenGIS Geometry type." @@ -38,12 +51,19 @@ class GeometryField(Field): _geom = 'GEOMETRY' def __init__(self, srid=4326, index=False, **kwargs): + #TODO: SRID a standard, or specific to postgis? + # Whether or not index this field, defaults to False + # Why can't we just use db_index? + # TODO: Move index creation (and kwarg lookup, and...) + # into Field rather than core.management and db.models.query. + self._index = index + + # The SRID for the geometry, defaults to 4326. + self._srid = srid + # Calling the Field initialization function first super(GeometryField, self).__init__(**kwargs) - # The SRID for the geometry, defaults to 4326 - self._srid = srid - self._index = index def get_internal_type(self): return "NoField" @@ -51,11 +71,16 @@ class GeometryField(Field): 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: - # Creating geometry indices doesn't yet work. - #return '%s\n%s' % (post_sql, _geom_index(self._geom, *args, **kwargs)) - return post_sql + return '%s\n%s' % (post_sql, _geom_index(self._geom, *args, **kwargs)) else: return post_sql @@ -69,12 +94,16 @@ class GeometryField(Field): def get_db_prep_save(self, value): "Making sure the SRID is included before saving." return 'SRID=%d;%s' % (self._srid, value) + + def get_manipulator_field_objs(self): + "Using the WKTField (defined above) to be our manipulator." + return [WKTField] # The OpenGIS Geometry Type Fields class PointField(GeometryField): _geom = 'POINT' -class LineString(GeometryField): +class LineStringField(GeometryField): _geom = 'LINESTRING' class PolygonField(GeometryField): diff --git a/django/contrib/gis/db/models/postgis.py b/django/contrib/gis/db/models/postgis.py index e4fd492dbf..34455dc85c 100644 --- a/django/contrib/gis/db/models/postgis.py +++ b/django/contrib/gis/db/models/postgis.py @@ -2,33 +2,31 @@ # django.db.models.query objects to be customized for PostGIS. from copy import copy from django.db import backend -from django.db.models.query import \ - LOOKUP_SEPARATOR, QUERY_TERMS, \ - find_field, FieldFound, get_where_clause +from django.db.models.query import LOOKUP_SEPARATOR, find_field, FieldFound from django.utils.datastructures import SortedDict # PostGIS-specific operators. The commented descriptions of these # operators come from Section 6.2.2 of the official PostGIS documentation. POSTGIS_OPERATORS = { # The "&<" operator returns true if A's bounding box overlaps or is to the left of B's bounding box. - 'overlapsleft' : '&< %s', + 'overlaps_left' : '&< %s', # The "&>" operator returns true if A's bounding box overlaps or is to the right of B's bounding box. - 'overlapsright' : '&> %s', + 'overlaps_right' : '&> %s', # The "<<" operator returns true if A's bounding box is strictly to the left of B's bounding box. 'left' : '<< %s', # The ">>" operator returns true if A's bounding box is strictly to the right of B's bounding box. 'right' : '>> %s', # The "&<|" operator returns true if A's bounding box overlaps or is below B's bounding box. - 'overlapsbelow' : '&<| %s', + 'overlaps_below' : '&<| %s', # The "|&>" operator returns true if A's bounding box overlaps or is above B's bounding box. - 'overlapsabove' : '|&> %s', + 'overlaps_above' : '|&> %s', # The "<<|" operator returns true if A's bounding box is strictly below B's bounding box. - 'strictlybelow' : '<<| %s', + 'strictly_below' : '<<| %s', # The "|>>" operator returns true if A's bounding box is strictly above B's bounding box. - 'strictlyabove' : '|>> %s', + 'strictly_above' : '|>> %s', # 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. - 'sameas' : '~= %s', + 'same_as' : '~= %s', # The "@" operator returns true if A's bounding box is completely contained by B's bounding box. 'contained' : '@ %s', # The "~" operator returns true if A's bounding box completely contains B's bounding box.