summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2007-03-31 21:25:29 +0000
committerJustin Bronn <jbronn@gmail.com>2007-03-31 21:25:29 +0000
commit8ef0b01e721f47fc1a9489dec6d0a54010defa1d (patch)
treea1ab50e4b567dd3f4914eecb09dae4228fef2cb1
parent0bf63559189f1ed3314958ca554c9fa1e2780827 (diff)
gis: two big changes:
(1) the addition of the GeoMixin class, which gives geometry fields contributed functions (e.g. get_GEOM_area). (2) geo_filter() is no more, all queries use filter() now. git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@4884 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/contrib/gis/db/models/GeoMixin.py25
-rw-r--r--django/contrib/gis/db/models/__init__.py4
-rw-r--r--django/contrib/gis/db/models/fields/__init__.py23
-rw-r--r--django/contrib/gis/db/models/manager.py7
-rw-r--r--django/contrib/gis/db/models/postgis.py17
-rw-r--r--django/contrib/gis/db/models/query.py21
6 files changed, 59 insertions, 38 deletions
diff --git a/django/contrib/gis/db/models/GeoMixin.py b/django/contrib/gis/db/models/GeoMixin.py
new file mode 100644
index 0000000000..f0accb69b4
--- /dev/null
+++ b/django/contrib/gis/db/models/GeoMixin.py
@@ -0,0 +1,25 @@
+# GEOS Routines
+from django.contrib.gis.geos import hex_to_wkt, centroid, area
+
+# Until model subclassing is a possibility, a mixin class is used to add
+# the necessary functions that may be contributed for geographic objects.
+class GeoMixin:
+ "The Geographic Mixin class, provides routines for geographic objects."
+
+ # A subclass of Model is specifically needed so that these geographic
+ # routines are present for instantiations of the models.
+ def _get_GEOM_wkt(self, field):
+ "Gets the WKT of the geometry."
+ hex = getattr(self, field.attname)
+ return hex_to_wkt(hex)
+
+ def _get_GEOM_centroid(self, field):
+ "Gets the centroid of the geometry, in WKT."
+ hex = getattr(self, field.attname)
+ return centroid(hex)
+
+ def _get_GEOM_area(self, field):
+ hex = getattr(self, field.attname)
+ return area(hex)
+
+
diff --git a/django/contrib/gis/db/models/__init__.py b/django/contrib/gis/db/models/__init__.py
index 5117ac60f6..89d17d6dbd 100644
--- a/django/contrib/gis/db/models/__init__.py
+++ b/django/contrib/gis/db/models/__init__.py
@@ -1,7 +1,7 @@
# Want to get everything from the 'normal' models package.
from django.db.models import *
-# The GeoManager class.
+# The GeoManager
from django.contrib.gis.db.models.manager import GeoManager
# The various PostGIS/OpenGIS enabled fields.
@@ -10,3 +10,5 @@ from django.contrib.gis.db.models.fields import \
MultiPointField, MultiLineStringField, MultiPolygonField, \
GeometryCollectionField
+# The geographic mixin class.
+from GeoMixin import GeoMixin
diff --git a/django/contrib/gis/db/models/fields/__init__.py b/django/contrib/gis/db/models/fields/__init__.py
index 50c8bb6ef0..0ff5a3ef8a 100644
--- a/django/contrib/gis/db/models/fields/__init__.py
+++ b/django/contrib/gis/db/models/fields/__init__.py
@@ -1,8 +1,8 @@
# 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
+from django.contrib.gis.oldforms import WKTField
+from django.utils.functional import curry
#TODO: add db.quotename.
@@ -35,15 +35,6 @@ def _geom_index(geom, style, model, 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."
@@ -65,6 +56,14 @@ class GeometryField(Field):
super(GeometryField, self).__init__(**kwargs)
+ def contribute_to_class(self, cls, name):
+ super(GeometryField, self).contribute_to_class(cls, name)
+
+ # Adding the WKT accessor function for geometry
+ setattr(cls, 'get_%s_wkt' % self.name, curry(cls._get_GEOM_wkt, field=self))
+ setattr(cls, 'get_%s_centroid' % self.name, curry(cls._get_GEOM_centroid, field=self))
+ setattr(cls, 'get_%s_area' % self.name, curry(cls._get_GEOM_area, field=self))
+
def get_internal_type(self):
return "NoField"
@@ -98,7 +97,7 @@ class GeometryField(Field):
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'
diff --git a/django/contrib/gis/db/models/manager.py b/django/contrib/gis/db/models/manager.py
index 10c6ec7018..80a2ca49f0 100644
--- a/django/contrib/gis/db/models/manager.py
+++ b/django/contrib/gis/db/models/manager.py
@@ -2,12 +2,7 @@ from django.db.models.manager import Manager
from django.contrib.gis.db.models.query import GeoQuerySet
class GeoManager(Manager):
+ "Overrides Manager to return Geographic QuerySets."
def get_query_set(self):
return GeoQuerySet(model=self.model)
-
- def geo_filter(self, *args, **kwargs):
- return self.get_query_set().geo_filter(*args, **kwargs)
-
- def geo_exclude(self, *args, **kwargs):
- return self.get_query_set().geo_exclude(*args, **kwargs)
diff --git a/django/contrib/gis/db/models/postgis.py b/django/contrib/gis/db/models/postgis.py
index 34455dc85c..c19d690d4e 100644
--- a/django/contrib/gis/db/models/postgis.py
+++ b/django/contrib/gis/db/models/postgis.py
@@ -2,7 +2,7 @@
# 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, find_field, FieldFound
+from django.db.models.query import LOOKUP_SEPARATOR, find_field, FieldFound, QUERY_TERMS, get_where_clause
from django.utils.datastructures import SortedDict
# PostGIS-specific operators. The commented descriptions of these
@@ -41,7 +41,6 @@ POSTGIS_GEOMETRY_FUNCTIONS = {
'distance' : 'Distance',
'equals' : 'Equals',
'disjoint' : 'Disjoint',
- 'intersects' : 'Intersects',
'touches' : 'Touches',
'crosses' : 'Crosses',
'within' : 'Within',
@@ -55,6 +54,7 @@ POSTGIS_GEOMETRY_FUNCTIONS = {
# and the geometry functions.
POSTGIS_TERMS = list(POSTGIS_OPERATORS.keys()) # Getting the operators first
POSTGIS_TERMS.extend(list(POSTGIS_GEOMETRY_FUNCTIONS.keys())) # Adding on the Geometry Functions
+POSTGIS_TERMS = tuple(POSTGIS_TERMS) # Making immutable
def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
if table_prefix.endswith('.'):
@@ -72,17 +72,17 @@ def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
return '%s(%s%s, %%s)' % (POSTGIS_GEOMETRY_FUNCTIONS[lookup_type], table_prefix, field_name)
except KeyError:
pass
-
+
# For any other 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)
-def geo_parse_lookup(kwarg_items, opts):
+def parse_lookup(kwarg_items, opts):
# Helper function that handles converting API kwargs
# (e.g. "name__exact": "tom") to SQL.
- # Returns a tuple of (tables, joins, where, params).
+ # Returns a tuple of (joins, where, params).
# 'joins' is a sorted dictionary describing the tables that must be joined
# to complete the query. The dictionary is sorted because creation order
@@ -112,10 +112,11 @@ def geo_parse_lookup(kwarg_items, opts):
# If there is only one part, or the last part is not a query
# term, assume that the query is an __exact
lookup_type = path.pop()
+
if lookup_type == 'pk':
lookup_type = 'exact'
path.append(None)
- elif len(path) == 0 or lookup_type not in POSTGIS_TERMS:
+ elif len(path) == 0 or not ((lookup_type in QUERY_TERMS) or (lookup_type in POSTGIS_TERMS)):
path.append(lookup_type)
lookup_type = 'exact'
@@ -200,6 +201,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
# Does the name belong to a one-to-one, many-to-one, or regular field?
field = find_field(name, current_opts.fields, False)
+
if field:
if field.rel: # One-to-One/Many-to-one field
new_table = current_table + '__' + name
@@ -293,8 +295,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
if hasattr(field, '_geom'):
where.append(get_geo_where_clause(lookup_type, current_table + '.', column, value))
else:
- raise TypeError, 'Field "%s" (%s) is not a Geometry Field.' % (column, field.__class__.__name__)
+ where.append(get_where_clause(lookup_type, current_table + '.', column, value))
params.extend(field.get_db_prep_lookup(lookup_type, value))
return joins, where, params
-
diff --git a/django/contrib/gis/db/models/query.py b/django/contrib/gis/db/models/query.py
index c2a2e66677..4b2f274a43 100644
--- a/django/contrib/gis/db/models/query.py
+++ b/django/contrib/gis/db/models/query.py
@@ -1,25 +1,24 @@
-from django.db.models.query import *
-from django.contrib.gis.db.models.postgis import geo_parse_lookup
+from django.db.models.query import Q, QNot, QuerySet
+from django.contrib.gis.db.models.postgis import parse_lookup
+import operator
class GeoQ(Q):
"Geographical query encapsulation object."
def get_sql(self, opts):
- "Overloaded to use the geo_parse_lookup() function instead of parse_lookup()"
- return geo_parse_lookup(self.kwargs.items(), opts)
+ "Overloaded to use our own parse_lookup() function."
+ return parse_lookup(self.kwargs.items(), opts)
class GeoQuerySet(QuerySet):
"Geographical-enabled QuerySet object."
- def geo_filter(self, *args, **kwargs):
- "Returns a new GeoQuerySet instance with the args ANDed to the existing set."
- return self._geo_filter_or_exclude(None, *args, **kwargs)
+ def __init__(self, model=None):
+ super(GeoQuerySet, self).__init__(model=model)
- def geo_exclude(self, *args, **kwargs):
- "Returns a new GeoQuerySet instance with NOT (args) ANDed to the existing set."
- return self._geo_filter_or_exclude(QNot, *args, **kwargs)
+ # We only want to use the GeoQ object for our queries
+ self._filters = GeoQ()
- def _geo_filter_or_exclude(self, mapper, *args, **kwargs):
+ def _filter_or_exclude(self, mapper, *args, **kwargs):
# mapper is a callable used to transform Q objects,
# or None for identity transform
if mapper is None: